Authenticating with Azure AD and OpenID Connect

Identity in the cloud

Identity management in the cloud is a totally different ball game to when everything was installed and accessed on the corporate network. Users in the enterprise authenticated with an on-premises directory service (e.g. Active Directory Domain Services) and this determined the apps and data they had access to. Occasionally, cross-forest federations were established to allow users belonging to one corporate domain to access resources in another.

Nowadays, with the proliferation of apps and services available in the cloud and the speed and ease with which we consume them, managing cross-forest trusts yourself is mostly a thing of the past. Identities still need to be managed in the cloud, but on-premises domain services alone don’t work when more and more apps are being delivered as multi-tenant SaaS that lives outside of your datacenter.

Azure Active Directory

On the public internet, where most modern software now operates, identity and access management is being consolidated around a few key multi-tenant identity services. Azure Active Directory, not to be confused with its on-premises cousin, is one of these.

The usual route for corporate entities looking to manage identity in the cloud is to synchronize their on-premises directory with Azure AD. From there, Azure AD can broker trusts between users and registered applications/services without the need for old-school federations.

Recently, I’ve been putting a lot of effort into learning how applications authenticate with Azure AD and make use of protected resources, owned by different applications, that also trust Azure AD. I’m going to share how a typical web application does this.

OpenID Connect

OpenID Connect is a standard authentication protocol for delegating access to user data (or some other protected resource) to client applications. It is an extension of the well-known OAuth 2.0 authorization framework, adding only some identity verification features. It operates over a RESTful HTTP API making it ideal for applications accessed over the internet, so most modern applications.

Authenticating with Azure AD is just like authenticating against any other OpenID Connect server. In fact, the only part of my sample code that you could directly associate with Azure AD itself is the authority URI used. This is a good thing for application developers because the same code can be used to authenticate with any authority that also supports OpenID Connect.

The protocol supports multiple “flows” for different authentication scenarios. The one I’m going to talk you through is the most common one, the authorization code flow.

Understanding Authorization Code Flow

The authorization code flow is used for delegating access from a user to a server-side client application, allowing it to access protected resources on the user’s behalf. This flow is most often used with Web APIs and MVC apps that can securely hold a client secret on the web server. Apps that operate directly on a user device like a mobile phone, something beyond the developer’s remit to secure, should use a different flow.

With authorization code flow, the client application redirects the user to the authentication authority (Azure AD or another), the user logs in and grants access, receives an authorization code and is redirected back to the client application. The client application then uses this authorization code and its own client credentials to obtain an access token for the user. It can then access protected resources with this access token.

A visual might help here:

AuthCodeFlow

This looks a little busy, but it isn’t so difficult to wrap your head around. Let’s go through that again, step-by-step.

  1. The user accesses a client web application.
  2. The application issues an authorization challenge.
  3. The user is redirected to Azure AD in their browser to sign in with their existing user account, e.g. jane.bloggs@contoso.com and password. They are asked to grant access to the specific things that the client application needs access to, defined through scopes.
  4. Sign in and granting of access by the user is successful, this produces an authorization code.
  5. The user is redirected back to the client application with the authorization code.
  6. The client application authenticates against Azure AD with the authorization code from the user and it’s own client credentials, an identity/shared secret for the application already known to Azure AD.
  7. If successful, Azure AD supplies the client application with a user access token. This contains claims that correspond to the scopes that the user previously approved access for.
  8. The client application uses the access token to access protected resources that it has a claim for. This is usually done by sending HTTP requests to the application servicing the protected resources, with the access token in the Authorization header of those requests.
  9. The application servicing the protected resources, most likely a Web API, accepts the access token (understanding the claims) and returns the requested resource.

This flow has several key security benefits for a hosted application:

  • The client application never handles the user’s Azure AD credentials. This is great both for the user and the application developer!
  • The access token gets transmitted directly to the client application, without going through the user’s device and potentially exposing it to something else.

Demo Application

That’s the theory over with – if you want more you can go straight to RFC 6749. Now we will turn our attention to the kind of code a client application will need to allow a user to sign-in with Azure AD and access their protected resources with an access token.

Introducing ToDoGraphDemo

The demo app I have written is a simple to do list app written with ASP.NET MVC, it synchronizes a task list with a OneNote page associated with the user. Office 365 makes a good test bed for this because all Office 365 access is federated with Azure AD. Even if you’ve never directly used Azure infrastructure before, if you have an Office 365 administrator account you can log into Azure Portal and see the Azure AD instance you got for free.

With my demo app, it is the client application. The user is anybody with an Office 365 account that includes OneNote, the authentication authority is Azure AD and the protected resource is the user’s OneNote data. The application that accepts the access tokens and services that resource is Microsoft Graph API, a RESTful API covering many Microsoft products. I’m going to cover the app’s use of Graph API in a separate post, if that interests you also.

The full code listing for the demo is available on GitHub.

Registering the client application

You can rely on the user to bring their credentials and you can rely on successful authentication to produce an authorization code, but we still need client credentials to request an access token. Our first step, therefore, is to register our app in the Microsoft Application Registration Portal.

You can log into the portal with any Microsoft account (personal or work). Once signed in, go through the Add an app wizard. There is a guided path for ASP.NET MVC that will give you the code you need to authenticate with Azure AD. However, the OWIN Startup class that wizard gives you does not request an access token, copy it out anyway because it’s a useful starting point. I kept the View My Claims page from this example.

Once the application exists in the My applications list, select it. You will be taken to a more detailed options page for your app registration. Under Application Secrets, select Generate New Password and copy the generated password to your clipboard. The combination of the Application Id and this password are the client credentials needed to generate an access token.

Editing Web.config

Following the portal wizard will get you a Web.config file in your solution containing your ClientId, RedirectUri, Tenant and Authority configuration properties. Add a new property, ClientSecret, whose value is the password you copied to the clipboard earlier.

The appSettings element in my Web.config looks as follows:

<appSettings>
    <add key="webpages:Version" value="3.0.0.0"/>
    <add key="webpages:Enabled" value="false"/>
    <add key="ClientValidationEnabled" value="true"/>
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
    <add key="ClientId" value="8f083dd5-032d-4f10-8af4-b00903eb8680" />
    <add key="ClientSecret" value="abc****************************" />
    <add key="RedirectUri" value="https://localhost:44378/" />
    <add key="Tenant" value="anchorloop.com" />
    <add key="Authority" value="https://login.microsoftonline.com/{0}/v2.0" />
    <!-- Allows the app to read and modify notebooks, sections and pages on behalf of the user -->
    <add key="Scopes" value="Notes.ReadWrite" />
  </appSettings>

Note: ClientId and ClientSecret above are not my real values, they’re a secret after all!

This is a good time to revise the purpose of each property:

  • ClientId is the Application Id of the registered app in the registration portal.
  • ClientSecret is a generated password associated with the registered application in the portal.
  • RedirectUri is where the user will be redirected, with the authorization code, after successfully signing in. Mine is a localhost address for testing on my local machine, but either way, it should be secured with TLS and accessed via HTTPS. If the transport layer were not encrypted at this point, it would undermine the whole protocol as a security mechanism.
  • Tenant allows you to restrict the set of users that can sign-in down to a single Azure AD tenant. The wizard example asks you to set this to common, which allows all Microsoft accounts to sign in. My app requires an Office 365 subscription, so I set this to be my tenant only.
  • Authority is the link (or more accurately, a template of the link) to the authentication service responsible for the exchange. This link is known as the Azure AD v2 endpoint.
  • Scopes is a space delimited string listing the scopes the application requires. These state to Azure AD exactly what the application is intended to access and these will be presented to the user transparently for approval the first time they sign-in to the app. This demo requires read and write access to OneNote to synchronize the task list, the appropriate scope for this is Notes.ReadWrite. If the app tried to use its access token to access anything it does not have a scope for, it would be denied.

Here is a screenshot of what the user sees the first time they successfully sign-in to the app via Azure AD:

permissions_and_claims

You can see how the user is asked to consider the claims requested by the app before an authorization code can be generated.

An OWIN Startup class for Authorization Code Flow

Here is the code listing for my OWIN Startup class, that uses the above settings:

public class Startup
{
    // The Client ID is used by the application to uniquely identify itself to Azure AD.
    string clientId = ConfigurationManager.AppSettings["ClientId"];
    // RedirectUri is the URL where the user will be redirected to after they sign in.
    string redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
    // Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
    static string tenant = ConfigurationManager.AppSettings["Tenant"];
    // Authority is the URL for authority, composed by Azure Active Directory v2 endpoint
    // and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
    string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture,
        ConfigurationManager.AppSettings["Authority"], tenant);
    // Scopes are the specific permissions we are requesting for the application.
    string scopes = ConfigurationManager.AppSettings["Scopes"];
    // ClientSecret is a password associated with the application in the authority.
    // It is used to obtain an access token for the user on server-side apps.
    string clientSecret = ConfigurationManager.AppSettings["ClientSecret"];

    public void Configuration(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {
            ClientId = clientId,
            Authority = authority,
            RedirectUri = redirectUri,
            PostLogoutRedirectUri = redirectUri,
            Scope = "openid email profile offline_access " + scopes,

            // TokenValidationParameters allows you to control the users who are allowed to sign in
            // to your application. In this demo we only allow users associated with the specified tenant.
            // If ValidateIssuer is set to false, anybody with a personal or work Microsoft account can
            // sign in.
            TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
            {
                ValidateIssuer = true,
                ValidIssuer = tenant
            },

            // OpenIdConnect event handlers/callbacks.
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                AuthenticationFailed = OnAuthenticationFailed
            }
        });
    }

    private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
    {
        string userId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
        TokenCache userTokenCache = new SessionTokenCache(
            userId, context.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
        // A ConfidentialClientApplication is a server-side client application that can securely store a client secret,
        // which is not accessible by the user.
        ConfidentialClientApplication cca = new ConfidentialClientApplication(
            clientId, redirectUri, new ClientCredential(clientSecret), userTokenCache, null);
        string[] scopes = this.scopes.Split(new char[] { ' ' });

        AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(context.Code, scopes);
    }

    private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
    {
        context.HandleResponse();
        context.Response.Redirect("/?errormessage=" + context.Exception.Message);
        return Task.FromResult(0);
    }
}

The meat of the class is in the Configuration method, which sets the authentication options in the OWIN middleware. This sets OpenID Connect as the authentication mechanism and the use of cookies. This method is similar to the one the registration portal wizard will guide you to write, the differences are the addition of my extra scopes from Web.config and an additional notification handler: OnAuthorizationCodeReceived. This is where we move on from the wizard’s code and trade the authorization code for an access token.

Authorization code handler

Now we’re using classes from MSAL, the Microsoft Authentication Library [NuGet]. On that note, Azure AD’s authentication libraries have a confusing history that you should be aware of. Before MSAL there was ADAL (Active Directory Authentication Library) and if you’re googling around for Azure AD authentication you will probably land on that because it has been around longer. However, that’s the library for “classic” Azure AD auth, using the v1 endpoint.

MSAL has more capabilities: it can enable authentication with Azure AD, any other Microsoft account and Azure AD B2C (business to consumer). Technically, MSAL is in the “preview” status, but only in the sense that Microsoft is not yet ready to commit to backward compatibility between all of the early versions. They do insist it is ready for production use, which is confusing because in most other contexts “preview” means: “available for general hackery but not yet ready for production use”. For what it’s worth, I think committing to MSAL is the way to go, that seems to be where the development effort is going. Anyway, back to the code.

Let’s look at this handler again without the rest of the Startup class:

private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
    string userId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
    TokenCache userTokenCache = new SessionTokenCache(
        userId, context.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
    // A ConfidentialClientApplication is a server-side client application that can securely store a client secret,
    // which is not accessible by the user.
    ConfidentialClientApplication cca = new ConfidentialClientApplication(
        clientId, redirectUri, new ClientCredential(clientSecret), userTokenCache, null);
    string[] scopes = this.scopes.Split(new char[] { ' ' });

    AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(context.Code, scopes);
}

The object model for MSAL departs from the OAuth 2.0 primitives a bit. We first create a token cache, which allows us to securely store the access token of the user for its lifetime, these are created on a per-user basis. We’ll go through the cache in a little more detail next.

Next, we create a ConfidentialClientApplication object, this corresponds to the kind of client that acquires an access token with the authorization code flow. Another type defined in MSAL is PublicClientApplication, which is intended for client-side apps that use the implicit flow instead. The ConfidentialClientApplication knows how to request an access token from the authorization code received when the event is triggered, our required scopes and it’s own client credentials. The token lives in the cache securely until we want to use it.

Defining a token cache

The SessionTokenCache actually isn’t included in MSAL. In the interests of transparency, I lifted the class from a code sample written by the Graph API team on GitHub and haven’t really changed it. I’ll avoid explaining the class in great detail like I wrote it myself, but basically, it’s a wrapper around the TokenCache object that synchronizes the persistent token cache with an ASP.NET user session using a read-write lock for thread safety.

I’m surprised Microsoft didn’t include something like this in the library for convenience, it seems to me this wrapper meets a use case that most users would have. I suspect this is because the HttpContextBase object the class uses is not part of .NET Core and they didn’t have a great multi-platform solution for marrying the cache with session state. Anyway, all credit to Microsoft for the code sample, it’s worked fine for me so far.

Using the access token

We now have all the code we need to acquire an access token for a user from Azure AD and use it to access the user’s data. As mentioned earlier, I’m going to cover ToDoGraphDemo’s use of Graph API with a separate blog post (because I’ve gone on long enough already), but I will at least close by showing you how to retrieve the access token from the cache and attach it to a HTTP request.

The following is a function from my GraphAuthProvider class:

public async Task<string> GetUserAccessTokenAsync()
{
    string userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
    HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
    TokenCache userTokenCache = new SessionTokenCache(userId, httpContext).GetMsalCacheInstance();

    ConfidentialClientApplication cca = new ConfidentialClientApplication(
        clientId, redirectUri, new ClientCredential(clientSecret), userTokenCache, null);

    // Attempt to retrieve access token from the cache. Could also make a network call for a new
    // access token if the cached one is expired or close to expiration and a refresh token is
    // available.
    try
    {
        AuthenticationResult result = await cca.AcquireTokenSilentAsync(
            scopes.Split(new char[] { ' ' }), cca.Users.First());
        return result.AccessToken;
    }
    // Unable to retrieve the access token silently.
    catch (Exception)
    {
        httpContext.Current.Request.GetOwinContext().Authentication.Challenge(
            new AuthenticationProperties() { RedirectUri = "/" },
            OpenIdConnectAuthenticationDefaults.AuthenticationType);

        throw new Microsoft.Graph.ServiceException(
            new Microsoft.Graph.Error
            {
                Code = Microsoft.Graph.GraphErrorCode.AuthenticationFailure.ToString(),
                Message = "Authentication is required."
            });
        }
    }

It rebuilds the ConfidentialClientApplication object, passing in the session cache, which is then used to acquire an access token silently. If the cached token has expired, the ConfidentialClientApplication may be able to use a refresh token and a round-trip to Azure AD to acquire a new access token, without requiring the user to sign in again.

The returned string can be supplied in the Authorization header of any HTTP request that accesses protected resources on behalf of the user.

// Get access token.
string accessToken = await GraphAuthProvider.Instance.GetUserAccessTokenAsync();

// Set access token to HTTP auth header.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);

Next time

That covers everything you need to sign-in and delegate access to a client application with Azure AD. My follow-up to this post will cover the app’s use of Graph API (via an OData client) and the access tokens acquired by the above code. Until I can get that written up, the complete solution is available on GitHub for your viewing pleasure.

See you next time.

The fastest way to bulk insert to SQL Server in .NET

Love Entity Framework and ORMs

I love Entity Framework and other object-relational mappers (ORMs) like it. These frameworks allow us to code against an object-oriented data model and map any changes seamlessly (usually) to the underlying relational model that the data is persisted in. They reduce the amount that we need to worry about databases.

Where I have seen the most value

Security IQ: Public Key Cryptography

Also in the series

The importance of data security

I’m going to tell you something that you already know: security is important. No matter how much we might rather be looking at shiny new JavaScript frameworks, data integrity is one of those few issues that can destroy the reputation of our products and companies overnight. What UI framework BigBank’s web app uses will never make the front page news, but a visible security breach definitely will. There’s a lot at stake in getting it right.

Considering its importance, it’s clear

Configuring SSH authentication for Git on a Windows Jenkins worker

With many of the tools commonly used in a Continuous Delivery pipeline, Windows is not the original OS the tool was developed for. Although support and adoption are growing all the time, there can still be some pain points and gotchas in configuring some of them to work as you would expect on a Windows OS.

In this post, we’re going to combine two of the big hitters in this space

Acting School: Azure’s use of a classic computational model

Recently, I have been fortunate enough to undertake some Azure training under one of the Solution Architects at Microsoft. We spent a lot of time focusing on Azure Service Fabric, Microsoft’s platform for developing microservice-based solutions. Much of what I heard reminded me of my experiences of Docker Swarm and Kubernetes, two very popular container orchestration platforms, and many of my questions in the training sessions were centered around why I might want to use Service Fabric over one of these other platforms that I knew a little better.

One point the trainer made that has stuck with me since is: containers often contain microservices, but microservices are not containers. It was clear to me then

Building the smallest PowerShell runner

The Problem

At the day job, one of my team’s current projects is a bespoke “serverless” script execution service for internal use, not unlike AWS Lambda or similar offerings. I’m not the main guy on this, but I’ve been involved in some interesting discussions about how we should control the execution environments. Ideally, they would be sandboxed and completely disposable, possibly only alive for the lifetime of the script they are executing. The obvious solution to this is to use containers.

The dominant scripting language amongst our user base is PowerShell, so we need to try

Thoughts on the State of DevOps Report 2017

This past Tuesday, the annual State of DevOps Report for 2017 was released. The report is one of the most respected in the industry and attempts to measure and comment on the direction that a broad range of organizations travel on their DevOps journey. There are usually some interesting comparisons and trends to analyze against the previous year’s results also.

The report is

Surviving Software’s Industrial Revolution

When I was a junior developer, the sort of books and blogs I read off-the-clock tended to be about design patterns, abstractions, test-driven development (TDD) and so on. These topics haven’t gone away, but these days we tend to focus more on rapid application delivery, continuous integration, feedback loops and continuous improvement in the development process of that software, not necessarily the software itself.

Much of the classic developer reading material has strong craftsman-like overtones, the excellent Pragmatic Programmer being the most obvious example. On the other hand, some of the more recent material, like Continuous Delivery or The DevOps Handbook, describe the process of developing software as a factory production line to be optimized, inspired by the lean manufacturing revolution at the likes of Toyota in the 1980s.

Is the change in imagery from the master artisan to the fine-tuned production line a sign of progress or something to be worried about? It looks like the software industry’s Industrial Revolution might have started.

Golang and Docker, better together

A match made in heaven

If you are a regular reader you will know just how much I have fallen for Golang recently. If not, see Fun with WebSockets in Golang for why I think it’s such a great language for writing backend services.

As explained in that blog post, my motivation for learning Golang originated with my experimentation with Docker. Golang programs are (usually) statically compiled to machine code, not bytecode, so no runtime interpreter like a JVM or Python is required to run them. This means that you can fit those programs into the smallest Docker containers possible for maximum density and reduced attack surface. Pair that with Golang’s performance (which is comparable to C++) and you have a match made in heaven.