从 ADAL 迁移到 MSAL 后如何获取 ServiceClientCredentials

How to get ServiceClientCredentials after migrating from ADAL to MSAL

对 ADAL 的支持将于 2022 年 6 月 30 日结束,Microsoft recommends migrating applications to MSAL

在迁移期间,我在将访问 Data Lake Storage 的代码从 ADAL 迁移到 MSAL 时遇到了困难。我几乎可行的尝试是:

// Before migration (authenticate using obsolete ADAL)
ClientCredential clientCredential = new ClientCredential(clientId, clientSecret);
ServiceClientCredentials credentials = await ApplicationTokenProvider.LoginSilentAsync(tenantId, clientCredential);

var adlsClient = AdlsClient.CreateClient(dataLakeStoreName, serviceCredentials)
// After migration (authenticate using newer MSAL)
var clientApplication = ConfidentialClientApplicationBuilder
        .Create(clientId)
        .WithClientSecret(clientSecrret)
        .WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
        .Build();
AuthenticationResult credentials = await (new[] { $"https://datalake.azure.net/.default" })
        .ExecuteAsync();

// We no longer have ServiceClientCredentials object, so the only overload left is the one that accepts a bearer token as a string:
var adlsClient = AdlsClient
        .CreateClient(dataLakeStoreName, $"{credentials.TokenType} {credentials.AccessToken}");

上面的 MSAL 代码工作了一段时间,但随着 ServiceClientCredentials 消失,我们丢失了负责刷新令牌的对象。因此,当令牌过期时,adlsClient 现在停止工作。

是否有任何简单的方法可以使用 MSAL 创建 ServiceClientCredentials? 或者我们是否需要在迁移到 MSAL 时自己编写刷新令牌的逻辑?

最近遇到了同样的问题。您可以实现自己的 ServiceClientCredentials,它可以利用 IConfidentialClientApplication 来管理访问令牌。例如:

public class DataLakeGen1ClientCredentials : ServiceClientCredentials
    {
        private readonly IConfidentialClientApplication _application;

        public DataLakeGen1ClientCredentials(string? appId, string? secretKey, string? directoryId)
        {
            _application = ConfidentialClientApplicationBuilder
                .Create(appId)
                .WithClientSecret(secretKey)
                .WithAuthority(new Uri($"https://login.microsoftonline.com/{directoryId}"))
                .Build();
        }

        public override async Task ProcessHttpRequestAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            HttpRequestHeaders httpRequestHeaders = request.Headers;
            var authResult = await _application.AcquireTokenForClient(new[] { $"https://datalake.azure.net/.default" })
                .ExecuteAsync(cancellationToken).ConfigureAwait(false);
            httpRequestHeaders.Authorization = new AuthenticationHeaderValue(authResult.TokenType, authResult.AccessToken);

            await base.ProcessHttpRequestAsync(request, cancellationToken).ConfigureAwait(false);
        }
    }

您需要覆盖 AdlsClient 使用的 ProcessHttpRequestAsync,以便在发送请求之前将令牌插入 HTTP headers。 AcquireTokenForClient 将为您提供一个有效的令牌。

使用 ServiceClientCredentials 的这个实现,您可以初始化 adlsClient 并像以前一样使用它:

var serviceCredentials = new DataLakeGen1ClientCredentials(clientId, clientSecret, tenantId);
AdlsClient client = AdlsClient.CreateClient(DataLakeStoreName, serviceCredentials);