How to get ServiceClientCredentials after migrating from ADAL to MSAL

Advertisements

The support for ADAL ends on June 30, 2022 and Microsoft recommends migrating applications to MSAL.

During migration, I encountered a difficulty migrating a code that accesses Data Lake Storage from ADAL to MSAL. My almost-working attempt is:

// 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}");

The above MSAL code works for a while, but with ServiceClientCredentials gone, we lost the object responsible for refreshing a token. So, when the token expires, adlsClient now stops working.

Is there any simple way to create a ServiceClientCredentials with MSAL?
Or do we need to code the logic that refreshes the token ourselves when we migrate to MSAL?

>Solution :

Got same problem lately. You can implement your own ServiceClientCredentials that can utilize IConfidentialClientApplication to manage access token. For example:

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);
        }
    }

Than you can use it to init your adlsClient:

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

Leave a ReplyCancel reply