Quote of the Day

more Quotes

Categories

Buy me a coffee

  • Home>
  • security>

Implement OAuth2 Client-Credentials flow with Azure AD and Microsoft Identity Platform.

OAuth2 Client Credentials flow is a protocol to allow secure communication between two web APIs. Specifically, the protocol specifies the flow of obtaining authorization for a client to access protected endpoints of a resource server with no user interaction involved. With Microsoft Identity Platform, Azure portal, Microsoft Authentication Library (MSAL), and .NET core security middleware, you can implement the OAuth2 client credentials flow without much difficulty. In this post, I go over how to leverage those technologies to protect your ASP.NET core web APIs.

Register applications in Azure AD (AAD)

In an OAuth2 client credentials flow, when the client asks the authorization server for an access token, the client authenticates using it’s credentials and specifies the resource types (scopes) which it needs access. The authorization server issues an access token for the client to access the resource server upon successful authentication. In the Oauth2 client-credentials flow, Azure AD acts as an authorization server. As such, it needs to identify the client and resource server, know the scopes available, and whether the client has been granted access. Using azure portal, you can register the applications, specify the scopes and set up access necessary for AAD to support the client credentials flow.

As an example, I have two applications which I register in AAD, one represent the client, and the other the resource server.

Register the client

The below animation show the steps to register an application in AAD. The client application needs to authenticate against Microsoft Identity Platform (v2.0) endpoint. For this example, I simply create a secret to use as the app’s password.

2019-12-07_13-36-53 (1).gif
Registering the client application which participates in an OAuth2 Client Credentials flow.

Register the resource owner

The below animation shows an example of registering the resource owner in a Client Credentials flow.

ClientCredentialsFlowExample_RegisteringResourceServer (1).gif
Registering the resource server application which participates in an OAuth2 Client Credentials flow

A scope in OAuth2 terminology represents a specific type of resource. In a different OAuth2 flow such as an implicit flow, an app can ask the user to grant access to a scope. For instance, the app may ask for Read and Write access to the user’s calendar in G Suite. Depending on the implementation, the user may selectively approve some scopes and deny others. For instance, the user may approve Read access and deny Write access. As shown in the above animation, via “Expose an API”, you can define the scopes for which the client can request access .

Pre-authorize a client access to resources

Whereas in an implicit flow, you can let the user approve or deny the scopes on demand,In a client credentials flow, you need to grant the client access in advance because the flow does not involve the user’s interaction. This is important to remember, since if you forget this step, you’ll get an error when making a request for an access token.

2019-12-15_17-51-59 (1).gif
Pre-authorize client access to resources

We have gone over the application registration. Let’s now go over the configuration in the applications.

Request access token

In the codes for the client application, we are going to use the MSAL library to handle the authentication and obtaining an access token. Specifically, we are going to use the Microsoft.Identity.Client package.

The following code snippets authenticate against AAD and obtain an access token for the client to access the resource server.

var appOptions = configuration
                    .GetSection(OptionKeys.PortalApiADPubOptions)
                    .Get<ConfidentialClientApplicationOptions>();
                    string authority = $"{appOptions.Instance}{appOptions.TenantId}/";
                    var application = ConfidentialClientApplicationBuilder
                    .CreateWithApplicationOptions(appOptions)
                    .WithAuthority(authority).Build();
                    var result = await application.AcquireTokenForClient(scopes:
                        scopes).ExecuteAsync();

The appsettings file contains the configurations including the client id and secret for the client to authenticate against AAD.

{
"AzureADApp": { "Instance": "https://login.microsoftonline.com/", "ClientId": "********-****-****-bd9e-6673a45b57f1", "ClientSecret": "*********.5Xe0dH1m8pWPO.bUQ4_HiA", "TenantId": "********-****-****-****-2283395ed452" }
}

The JSON above gets deserialized into an instance of ConfidentialClientApplicationOptions.

The next section of the codes build an implementation of the IConfidentialClientApplication using the options from the appsettings. We use the AcquireTokenForClient method, passing in the scopes parameter. The scopes parameter is an array of strings which contain the full URI we define in the Expose an API section when we register the resource owner.

In the above code snippets, you see how we can obtain an access token for the client app. In the other application – the resource server which hosts the protected resources, we need to validate the access token. Let’s go over that in the next section.

Validate access token

We use ASP.NET core authentication middleware to check a request contains a valid JWT token before allowing access to the resources.

The following code adds the .NET core authentication middleware to the request pipeline.

app.UseAuthentication()

The app.UseAuthentication() ensures the caller has already authenticated before allowing access to the resource and also set the User object. We need to set the authentication middleware before the Mvc middleware (app.UseMvc()) since we want to make sure the caller has already authenticated before allowing access . Otherwise, the Authorization tag does not work.

Besides adding the authentication middleware, you also need to register the services to handle the authentication. You can use the .NET Core JwtBearer middleware to handle receiving and validating a JWT token. For instance, below I show the code snippets for validating a JWT token.

public static IServiceCollection AddSecurity(this IServiceCollection services, IConfiguration configuration)
        {
AzureADOptions azureADOptions = configuration.GetSection("AzureADApp").Get<AzureADOptions>(); services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme).AddAzureADBearer(options => { configuration.Bind("AzureADApp", options); }); services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options => { // This is an Azure AD v2.0 Web API. options.Authority += "/v2.0"; options.TokenValidationParameters.ValidateTokenReplay = true; options.TokenValidationParameters.ValidIssuer = string.Format(@"https://sts.windows.net/{0}/", azureADOptions.TenantId); options.TokenValidationParameters.ValidateAudience = true; options.TokenValidationParameters.ValidateLifetime = true; options.TokenValidationParameters.ValidateIssuerSigningKey = true; options.TokenValidationParameters.ValidAudiences = new string[] { options.Audience, $"api://{options.Audience}" }; return services; }

In the above snippets, options.Authority refers to the issuer which issues the access token. For AAD v2.0 endpoint which is Microsoft Identity Platform endpoint, the authority url has the following format:

https://login.microsoftonline.com/{tenantId}/v2.0

The next few lines of codes configure what information we want to validate in the JWT token. With the above codes, I am able to validate:

  • The token comes from my Azure AD tenant.
  • The value of audience reflects the token is for authorization to access the resource server, and not any other API.
  • The token is currently active. It has not expired or will become active in the future.
  • The key used to sign the token is valid.

Because the configurations can be lengthy depending on your implementation, it’s a good idea to organize them into an extension method, as in the above example snippets. In the startup class, you can simply call the extension method to configure the services. 

Below shows the “AzureADApp” section in appsettings.

{ 
"AzureADApp": { "ClientId": "********-****-****-5678-e381a16a1765", "TenantId": "********-****-****-1234-2283395ed453", "Instance": "https://login.microsoftonline.com/" }
}

That’s it. Hopefully this post has given you some information to implement OAuth2 client credentials flow in your .NET core project, leveraging AAD, MSAL and .NET core security middlewares.

To learn more about JWT, check out my post.

References

Microsoft identity platform and the OAuth 2.0 client credentials flow

Overview of ASP.NET Core Authentication

A look behind the JWT bearer authentication middleware in ASP.NET Core

On The Nature of OAuth2’s Scopes

Quickstart: Register an application with the Microsoft identity platform

Microsoft.Identity.Client

5 comments