2
votes

I am in a bit of a jam, I need to access Azure Event Hub and Azure Data Lake from the same service principal in the same piece of code (C#). The service principal is an Azure AD application and I am authenticating with a certificate.

If I use ADAL 2.x and authenticate I can connect to Azure Data Lake fine, but Event Hubs (WindowsAzure.ServiceBus) is not able to authenticate (see further down).

The ADAL 2.x code I use for creating the credential for Azure Data Lake is as below, this works 100% fine:

  public static ServiceClientCredentials GetCreds_SPI_Cert(string tenant, Uri tokenAudience, string clientId, string certificateThumbprint)
        {
            var certificate = FindCertificateByThumbprint(certificateThumbprint);

            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

            var clientAssertionCertificate = new ClientAssertionCertificate(clientId, certificate);
            var serviceSettings = ActiveDirectoryServiceSettings.Azure;
            serviceSettings.TokenAudience = tokenAudience;

            var creds = ApplicationTokenProvider.LoginSilentWithCertificateAsync(tenant, clientAssertionCertificate, serviceSettings).GetAwaiter().GetResult();
            return creds;
        }

If I upgrade to ADAL v3 in order to gain access the Event Hub (WindowsAzure.ServiceBus requires it), then the following code works fine (for event hub):

public static TokenProvider GetTokenProviderViaCetificate(string tenant, Uri tokenAudience, string clientId, string certificateThumbprint)
{
    var certificate = FindCertificateByThumbprint(certificateThumbprint);
    return TokenProvider.CreateAadTokenProvider(
        new AuthenticationContext($"https://login.windows.net/{tenant}"),
        new ClientAssertionCertificate(clientId, certificate),
        ServiceAudience.EventHubsAudience);
}

Note that the token provider in the Event Hub authentication CreateAadTokenProvider is only available in ADALv3. The Service Bus and Data Lake clients also both require different types of credential.

If I use ADAL V3 and go back to the Data Lake code, the GetCreds_SPI_Cert fails with the following error:

Method not found: 'System.Threading.Tasks.Task`1 Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireTokenAsync(System.String, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate)'.

In short, how do I use ADAL v3 to authenticate an Azure AD App service principal against Azure Data Lake (using a certificate)? I can't downgrade to ADAL 2.x due to the Event Hub dependency.

1
I'm trying to rack my brain, pretty sure I came across an adaptor to go between multiple versions of the ADAL, it was a while ago unfortunately my memory is a little fuzzy of it.Alex KeySmith
@AlexKeySmith, any clue would help, although apart sideloading separate app domains or shelling to some sort of CLI I don't see any reasonable options.Murray Foxcroft
I'll try to remember to dig through my OneNote when back in the office tomorrow, it was for a slightly different problem but sounds related.Alex KeySmith
This might offer some hints, albeit probably not the answer directly: github.com/Azure/azure-sdk-for-net/blob/…Alex KeySmith
If it proves useful let me know and I'll pop it as an answer.Alex KeySmith

1 Answers

1
votes

Something that could aid devise a solution, albeit perhaps not the solution directly, is an authentication adapter documented deep in the github repo:

https://github.com/Azure/azure-sdk-for-net/blob/3f736b5af3851ab99bbaa98483ae537de0d48cfb/Documentation/AuthConflictsBetweenSDKs.md

Authentication conflicts can happen due to mixing old SDKs* and new SDKs*. SDKs that depend on The credential types created by the new authentication library (Microsoft.Rest.ClientRuntime.Azure.Authentication, type = ServiceClientCredentials) are incompatible with the credential types used in older SDKs (Microsoft.Azure.Common, type = SubscriptionCloudCredentials). The problem is that using two different authentication libraries will require you to authenticate twice, which is a painful experience.

public class SubscriptionCredentialsAdapter : SubscriptionCloudCredentials
{
    ServiceClientCredentials _credentials;
    string _subscriptionId;

    public SubscriptionCredentialsAdapter(ServiceClientCredentials wrapped, string subscriptionId)
    {
        _credentials = wrapped;
        _subscriptionId = subscriptionId;
    }

    public override string SubscriptionId
    {
        get { return _subscriptionId; }
    }

    public override Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
       return _credentials.ProcessHttpRequestAsync(request, cancellationToken);
    }
}