OAuth On-Behalf-Of Flow: Getting a user access token without user interaction. Includes ADAL sample.




OAuth 2.0 (and hence Azure Active Directory) provides the On-Behalf-Of flow to support obtaining a user access token for a resource with only a user access token for a different resource – and without user interaction.
This supports the scenario where a secured Web API acts as an interface to other resources (a.k.a endpoints) secured by the same identity provider and that require user context. As a practical example, a mobile client accesses some resources via a middle tier API which provides services such as data processing, caching, API simplification/optimisation, joining of datasets, etc.

The OAuth flow that achieves this is called the On-Behalf-Of flow; this makes sense as we’re facilitating the middle tier to act on behalf of the client when it accesses the resources farther down.

Using the on-behalf-of flow to access a resource via a middle tier API

Some background

Authentication with an OAuth 2.0 identity provider (such as AAD) produces JWT tokens. These tokens include information such as which claims (permissions) the user should be granted and the particular resource at which the token is valid (such as graph.microsoft.com). The OAuth 2.0 framework is specified such that a given token can only ever be valid for a single resource. This means that the token received by an endpoint (such as an Azure App Service Web API) cannot be used to directly authenticate to ‘another resource’. This is because the token’s resource will be that of the Web API and not the ‘other resource’. To see this I recommend checking out jwt.io and cracking open some tokens.

For completeness, the ‘other resource’ could be accessed using app-only authentication if it supports it, and if user context is not required (i.e. the return value will be the same regardless of the user) although this may greatly increase complexity in a multi-tenant scenario.

Configuring AAD for on-behalf-of

Before we get to the code the first hurdle is configuring AAD app registrations correctly. Initially it may be tempting to consider having both the Client and Web API layers utilise a single AAD app registration. After all, they are same holistic ‘app’ and how else can we get a user to consent to the permissions required by the Web API app when there is no interactive interface at that point? The latter point is resolved by explicitly binding the app registrations so that both are consented to as one. I mention how this is done below. By having two app registrations the flexibility of configuration is improved; we can have a Native app registration for the client and a Web API app registration for the Web API, we can have implicit flow configured for one app and not the other, and generally have granular control over configuration. Most vitally, an app registration can’t issue tokens valid for its own resource so two app registrations is a requirement.

I’ll avoid stepping though the configuration of the app registrations here as this is available elsewhere including this GitHub project. I will give a high level overview of what needs to happen.

    1. Create app registration for the Web API
      • Assign permissions to the downstream resources (e.g. Microsoft Graph, a custom Web API, etc)
      • If supporting multi-tenant authentication ensure availableToOtherTenants is set to true in the manifest
    2. Create app registration for the Client
      • Assign permissions to the app registration created above for the Web API
      • If supporting multi-tenant authentication ensure availableToOtherTenants is set to true in the manifest
    3. Associate app registrations
      • In the manifest for Web API app registration, configure knownClientApplications to reference the App ID for the app registration created for the Client. E.g. "knownClientApplications": ["9XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXc"]
        This binds the app registrations such that the Web API app registration is consented to as part of a single consent dialog displayed to a user when they authenticate to the Client app registration.
Before and after the app registrations are associated. Note how ‘Access Mobile App Backend’ is no longer present and instead is expanded to show the individual permissions required by that app.

Access Token Broker code

The following code is a .NET example of how to use the Active Directory Authentication Library (ADAL) to achieve the On-Behalf-Of flow.

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Claims;
using System.Threading.Tasks;

namespace PR.Azure.Core.AccessTokenBrokers
{
    public class AdalAccessTokenBroker //: IAccessTokenBroker
    {
        public async Task<string> GetAccessTokenOnBehalfOfAsync(
            string accessToken, string appId, string appKey, string resource)
        {
            ClientCredential clientCred = new ClientCredential(appId, appKey);
            string tenantClaim = "http://schemas.microsoft.com/identity/claims/tenantid";
            string authority = string.Format("https://login.microsoftonline.com/{0}", 
                ClaimsPrincipal.Current.FindFirst(tenantClaim)?.Value);

            // NOTE : Implement a Redis token cache or token will be fetched from AAD every time.
            // E.G. https://blogs.msdn.microsoft.com/mrochon/2016/09/19/using-redis-as-adal-token-cache/
            //TokenCache tokenCache = new RedisTokenCache(ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier)?.Value);
            //AuthenticationContext authContext = new AuthenticationContext(authority, tokenCache);
            AuthenticationContext authContext = new AuthenticationContext(authority);
            UserAssertion userAssertion = new UserAssertion(
                accessToken, 
                "urn:ietf:params:oauth:grant-type:jwt-bearer", 
                ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn)?.Value);

            // NOTE : May want to handle retry in case of transient errors
            AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred, userAssertion);
            return result?.AccessToken;
        }
    }
}

Credits and further reading

This GitHub project was a very useful resource and a recommended starting point:
https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof-ca

Microsoft docs OAuth 2.0 On-Behalf-Of flow
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-on-behalf-of

OAuth 2.0 specification
https://tools.ietf.org/html/rfc6749

Redis Token Cache example
https://blogs.msdn.microsoft.com/mrochon/2016/09/19/using-redis-as-adal-token-cache/

Vardhaman Deshpande – always helpful
http://www.vrdmn.com/

I was inspired to write this post not because this information isn’t available but because the information is hard to find if you aren’t familiar with the term “On-Behalf-Of”. Hopefully this post will be found by those of you searching for terms like “trade access token for new resource”, “change token resource”, “use access token with multiple resources/endpoints”, “access Microsoft Graph via Web API”, etc.

Paul.




Leave a Reply

Your email address will not be published.