Skip to main content

Service to service auth via Azure Active Directory

One of the things I like with the newer services from Azure is the use of Azure AD to authenticate. Azure Keyvault is a perfect example, I can request a JWT from Azure AD and I just pass that to Keyvault in the Authentication header and I am in. Bringing me back to the good old days when we would use Windows AD user as service accounts, you change your password in one place and it's updated everywhere. But this time, you can have more than one password, so your services don't crash as you try to cycle the passwords. (I think I hear angels singing from heaven...)

So, how do we make use of this for our own services? So glad you asked.

Let's start by pointing you to Microsoft's documentation for Authentication Scenarios for Azure AD, it's a very good read, and you really should know this stuff before starting this.  The scenario we are looking for is "Daemon or Server Application to Web API", particularly looking at how Web APP 1 (TodoListDaemon) and Web APP 2 (TodoListService) are talking to each other.  The sample can be found at https://github.com/Azure-Samples/active-directory-dotnet-daemon.

There was one question that was not answered for me, so keeping with my normal style, let's go under the hood and take a look.

Validating the Azure AD JWT

One of the details that almost every Identity Provider (IDP) that implements OAuth2 likes to skip over in their documentation is how the JWT is validated.  For me, this is one of the most critical details you need to understand when integrating any IDP, I include the "out of the box" auth provider in ASP.NET, in this group as well.  But to most people, it's a magic black box that they don't even know is there.  (Getting off of soap box)

So let's talk about how Todo List Service from the sample is validating the JWT.  The magic happens inside of "Microsoft.Owin.Security.ActiveDirectory".  From with in App_Start/Startup.Auth.cs we are going to register our authentication method using the following code.
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
 new WindowsAzureActiveDirectoryBearerAuthenticationOptions
 {
  Audience = ConfigurationManager.AppSettings["ida:Audience"],
  Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
 });
Now if we dig into some of the key parts of Use Windows Azure Active Directory Bearer Authentication.
/// <summary>
/// Extension methods provided by the Windows Azure Active Directory JWT bearer token middleware.
/// </summary>
public static class WindowsAzureActiveDirectoryBearerAuthenticationExtensions
{
    private const string SecurityTokenServiceAddressFormat =
        "https://login.windows.net/{0}/federationmetadata/2007-06/federationmetadata.xml";

    /// <summary>
    /// Adds Windows Azure Active Directory (WAAD) issued JWT bearer token middleware to your
    /// web application pipeline.
    /// </summary>
    /// <param name="app">The IAppBuilder passed to your configuration method.</param>
    /// <param name="options">An options class that controls the middleware behavior.</param>
    /// <returns>The original app parameter.</returns>
    public static IAppBuilder UseWindowsAzureActiveDirectoryBearerAuthentication(
        this IAppBuilder app, 
        WindowsAzureActiveDirectoryBearerAuthenticationOptions options)
    {
        ...
        options.MetadataAddress = string.Format(
            CultureInfo.InvariantCulture,
            SecurityTokenServiceAddressFormat,
            options.Tenant);
        ...
        var cachingSecurityTokenProvider = new WsFedCachingSecurityTokenProvider(
            options.MetadataAddress,
            options.BackchannelCertificateValidator, 
            options.BackchannelTimeout, 
            options.BackchannelHttpHandler);
        ...
        jwtFormat = new JwtFormat(
            options.Audience, 
            cachingSecurityTokenProvider);
        ...
        var bearerOptions = new OAuthBearerAuthenticationOptions
        {
            Realm = options.Realm,
            Provider = options.Provider,
            AccessTokenFormat = jwtFormat,
            AuthenticationMode = options.AuthenticationMode,
            AuthenticationType = options.AuthenticationType,
            Description = options.Description
        };

        app.UseOAuthBearerAuthentication(bearerOptions);
        ...
    }
}
The key to figuring out how we check the signature of the JWT is federationmetadata.xml.  If you open up this file, you will find the public key used to sign any JWT generated from your Azure AD instance.  It comes down to "WsFedCachingSecurityTokenProvider" caching the XML file and the public key, and then use the existing implementation of UseOAuthBearerAuthentication.

Clean and simple, well done guys.

The story changed a bit with ASP.Net Core

Links to checkout:

Popular posts from this blog

Querying for items in an Array in CosmosDB

If you have spent any time looking at the documentation for Microsoft CosmosDB / DocumentDB, you will see a lot of examples where the data model has a property named "Tags" that is a list of strings.  But you don't see many times they query on something in that Tag property...  One example I saw a query on Tags[0] = "some value" I don't know how often I will need that, but you know, good to know you can do it. After looking through the SQL syntax reference .  The 2 ways I most likely query the Tags would be to use a join on the Tags property or use the ARRAY_CONTAINS function. Side note; the performance of the two methods are basically identical, leading me to believe the query optimizer generates the same instruction sets for both. So unless you have an array of complex objects, just use ARRAY_CONTAINS. Cool, we know how to query for documents that have our tag on them now... One small problem, when you load a million, or even a hundred thousand do...

Service to service auth via Azure Active Directory with ASP.Net Core

Sample configuration for ASP.Net Core 1.1 to use Azure AD for Service to Service Authentication.  Update your Startup.cs to have the following public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(); ... } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseJwtBearerAuthentication(new JwtBearerOptions { Authority = "https://login.microsoftonline.com/{AAD Tenant Name or ID}", Audience = "{Application ID URL}" }); ... } Microsoft.AspNetCore.Authentication.JwtBearer defaults to using OpenID Connect discovery document to validate the bearer token. The Authority is the prefix for the the discovery document.  The middleware will append ".well-known/openid-configuration/" to whatever you pass in to the Authority.  If your IDP has a diffrent endpoint for the discovery document, you can specify the MetadataAddress option, tha...