Using bearer tokens in ASP.NET Core

There are not much examples available about ASP.NET Core and acquiring access token. I found good and pretty big sample by Microsoft Patterns & Practices called multitenant-saas-guidance, Based on this I wrote my simple “boiler plate” ASP.NET Core solution that authenticates against Azure Active Directory and asks current user data using Microsoft Graph.

The following diagram from Azure Active Directory documentation illustrates the situation.

Azure AD: Client credentials grant flow

Sample solution. My solution at GitHub. My “boiler plate” solution is available at GitHub repository AspNetCoreBearerTokenAuth. You can take it, configure it and run it wherever you like. Also you can use it as starting point for your own application.

To have a bearer token the application must catch access token and put it in token cache for later use. This is the code that is not generated by Visual Studio tools automatically and writing it from scratch very good understanding of Azure AD authentication is needed.

Storing access token

To store access token the token cache is used. My solution uses SQL Server based distributed cache so this solution can also be used in cloud environments. Tokens are cached by special class called TokenCache. Sample application inherits DistributedTokenCache class from this. DistributedTokenCache is needed because is works like a bridge between SQL storage and TokenCache. It is all configured in Startup class of sample application. This is where solving the puzzle starts.

TokenService and AuthEvent classes do the actual work. When access code is received then AuthorizationCodeReceived event in AuthEvent is fired. In this event the token service is called to retrieve access token. Access token is put to token cache for later use. This is the additional work needed on application level to be able to acquire bearer tokens.

Here is AuthorizationCodeReceived event of AuthEvents class.

public override async Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
    var principal = context.Ticket.Principal;
    var request = context.HttpContext.Request;
    var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);

    var tokenService = (ITokenService)context.HttpContext.RequestServices.GetService(typeof(ITokenService));
    try
    {
        await tokenService.RequestTokenAsync(
            principal,
            context.ProtocolMessage.Code,
            currentUri,
            _azureAdConfig.GraphResourceId)
            .ConfigureAwait(false);
    }
    catch
    {
        await tokenService.ClearCacheAsync(principal).ConfigureAwait(false);
        throw;
    }
}

Here is RequestTokenAsync method of TokenService class.

public async Task<AuthenticationResult> RequestTokenAsync(
    ClaimsPrincipal claimsPrincipal,
    string authorizationCode,
    string redirectUri,
    string resource)
{
    try
    {
        var userId = claimsPrincipal.GetObjectIdentifierValue();
        var issuerValue = claimsPrincipal.GetIssuerValue();
        var authenticationContext = await CreateAuthenticationContext(claimsPrincipal)
            .ConfigureAwait(false);
        var authenticationResult = await authenticationContext.AcquireTokenByAuthorizationCodeAsync(
            authorizationCode,
            new Uri(redirectUri),
            new ClientCredential(_adOptions.ClientId, _adOptions.ClientSecret),
            resource)
            .ConfigureAwait(false);

        return authenticationResult;
    }
    catch (Exception)
    {
        throw;
    }
}

Getting bearer token

If controller action needs bearer token then ITokenService must be injected to controller.

[Authorize]
public class HomeController : Controller
{
    private readonly ITokenService _tokenService;

    public HomeController(ITokenService tokenService)
    {
        _tokenService = tokenService;
    }

    // Controller actions follow
}

Bearer token can be asked only for authenticated user. With anonymous user it’s possible only to ask token for application level permissions but this case is not covered here.

Here is the sample controller action to query current user’s information from Azure Active Directory.

public async Task<IActionResult> MyInformation()
{
    var token = await _tokenService.GetBearerToken(User);
    var authDelegate = new DelegateAuthenticationProvider(
                            (requestMessage) =>
                            {
                                var authHeader = new AuthenticationHeaderValue("bearer", token);
                                requestMessage.Headers.Authorization = authHeader;
                                return Task.FromResult(0);
                            });

    var client = new GraphServiceClient(authDelegate);
    var me = await client.Me.Request().GetAsync();
    var collection = GraphObjectToCollection(me);

    return View(collection);
}

Here bearer token is given to authetication provider delegate that is used by Microsoft Graph client to authenticate user.

Wrapping up

Azure AD is powerful and flexible solution for online authentication and authorization. It also supports bearer token authentication scenarios between applications and services. Current ASP.NET Core tooling doesn’t generate code for bearer token scenarios and therefore developers must write some code by theirselves. Solution offered here is simple enough to get connected to external services using bearer token authentication. For real scenarios more care of exceptions and special cases is needed and it’s good idea to check how Microsoft has implemented bearer token authentication.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    One thought on “Using bearer tokens in ASP.NET Core

    Leave a Reply

    Your email address will not be published. Required fields are marked *