Azure AD Delegation scenario


tl;dr

  1. Register a new Web App in AAD for the Api
    1. Add the required "delegated" permissions to the external resource (i.e: Microsoft Graph)
  2. Register a new Web App in AAD for the Frontend
    1. Add the permissions to access the Api app
  3. Configure the Web apps code with the authentication details as usual (ClientID, Client Secret, Audience Uri..etc..). ADAL is the right choice for .NET code
  4. In Azure portal edit the Api registration manifest adding the GUID of the FronEnd app:
    "knownClientApplications": ["XXXXXX-YYYY-ZZZZZZ-WWWWW-XXXXXXX"]
  5. In the Api app code, save the bootstrap token during logon and use it to ask a new "On-Behalf-Of" token for the external resource with the user assertion "urn:ietf:params:oauth:grant-type:jwt-bearer"

"Hello World"!
Protect a web resource with Azure AD is really easy and well documented, but in a delegation scenario where we need to access to an external resource impersonating the signed-in user the things can get little more complicated.
Is not difficult, we just need few extra steps and configuration from the base case.

If I would like to implement an architecture as the following:

 

The very first steps are:

  1. Register a new Web App in AAD for the Api
    1. Add a redirect URL with "http://localhost"
    2. Add the required "delegated" permissions to the external resource (i.e: Microsoft Graph)
  2. Register a new Web App in AAD for the Frontend
    1. Add redirect URLs to point to the main page
    2. Add the permissions to access the Api app
  3. Configure the apps code with the authentication details as usual (ClientID, Client Secret, Audience Uri..etc..)

As you know the first time we access a resource protected by an Oauth based protocol (like OpenIDConnect in AAD) we need to "visually" grant the access to that resource. The classic HTML page that requires the user input to continue. Because at step 2/b we specified the FrontEnd app needs to access the Api app the user will be prompted to confirm the access to both applications (Frontend + Api) in a single page (note: the external resource is mentioned here).
This allows the FrontEnd app to "reedem" the authorization code and ask for a token to access the Api app. So far so good.

Unfortunately this is not sufficent if the Api app needs to access an external resource acting as the signed in user. This because the token received from this layer is meant only to the Api app and cannot be used to access the external resource (i.e: Microsoft Graph). We need a specific token for this latter resource but there are two major issues at this point:

  1. The user didn't be prompted for that, so he never granted the access to the external resouce
  2. The API is called via code (REST API) and therefore cannot display any HTML page to ask for such grant

In any case we also would like to silently access this resource in a SSO manner without asking again for credentials.

Basically we need a way to grant the access to the external resource via the Api layer upfront. Fortunately this is possible manually editing the registration manifest for the Api app in the Azure portal:

"knownClientApplications": ["XXXXXX-YYYY-ZZZZZZ-WWWWW-XXXXXXX"]

Adding the GUID of the FrontEnd app to the "knownClientApplications" property we link the two registrations, that means the first time the user access the FrontEnd app he will be prompted to grant the access to all the permissions specified for both apps and not only for the FrontEnd ones. In other words, we obtain a consensus page that lists all the permissions and all the external resources accessed by the current application and all the linked ones.

Now, as the user grants the access to all the resources upfront the Api app can request a token for the external resource in a SSO manner. In this case we need to use the "On-Behalf-Of" flow that allows the app to get an access token that delegates the user credentials. To successfully request this token we need to:

  • provide the Bootstrap Token (authentication token sent by the FrontEnd to the Api app)
  • Specify the assertion "urn:ietf:params:oauth:grant-type:jwt-bearer"

if you are using ADAL this translates in two simple steps:

  • SaveSigninToken = true in Startup.Auth.cs / WindowsAzureActiveDirectoryBearerAuthenticationOptions/TokenValidationParameters (as by default for performance is discarder after the access)
  • Request the token with:
UserAssertion userAssertion = new UserAssertion(bootstrapContext, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
var result = await authContext.AcquireTokenAsync(resourceUri, clientCred, userAssertion);

 

Comments (0)

Skip to main content