ProgrammerAl
ProgrammerAl

Reputation: 787

Authenticate Azure AD User to multiple backend services

I'm trying to find a strategy for authorizing a web client that makes HTTP calls to two services hosted in Azure. The web client is all client-side, and the two API Services are Azure Functions hosted in Azure.

For each of the three mentioned apps, I have setup an App Registration in Azure AD and exposed the scopes for the two Azure Functions applications to the web client to allow the client to make calls to them. I have also setup app roles for the two API applications and assigned those roles to a user.

The current setup works very well when the web client makes a single call to each API service. However, I want the web client to make a call to Service A, and then Service A makes an HTTP call to Service B using the credentials of the user of the web client. From what I see, when the web client makes a call to Service A, the token given to the web client only has the Scope and Role for Service A, and nothing for Service B.

My question is, what is the recommended strategy for allowing the user's credentials to be passed along to an internal service? Especially when the original token doesn't have the credentials for the extra service.

I did find this question (App service to app service auth in Azure using Managed Identity), which is very close to what I'm trying to accomplish. The marked answer seems to imply that my own application is responsible for generating a new token to the extra service, but I'm not sure which application is responsible for that. Is the recommendation that the web client make a separate request to acquire the token to Service B, and in the HTTP Request to Service A, include the token to Service B in the request body when making the call?

Upvotes: 1

Views: 1052

Answers (2)

ProgrammerAl
ProgrammerAl

Reputation: 787

I found the strategy after reading this guide from Microsoft: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow

The general idea is to have the middle tier app (Service A) make an HTTP request to Azure AD for a new token for the user. This new token has the proper scopes and roles for authenticating against Service B, and is included in the HTTP requests to Service B (for that user).

From the documentation, I chose to use the "client secret" route. Sending a request to https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token with the following application/x-www-form-urlencoded content type fields:request fields

I also had to take 2 extra steps for setting up my applications. In the Azure Portal I went to the App Registration for my Service A and:

  1. Under API Permissions -> Granted it permission to the scope for Service B
  2. Under Certificates & Secrets -> Added a new Client Secret. This is the client_secret field sent in the HTTP request.

Another answer had an example like this, but it was for ASP.NET applications, not Azure Functions.

Upvotes: 1

Gaurav Mantri
Gaurav Mantri

Reputation: 136146

I ran into a scenario like this not too long ago where I had to get a token for the user for a backend API from frontend and then calling Azure Management API in the backend inside that API call.

The solution I ended up implementing is to enable token acquisition to call downstream API and then acquire token in the backend API using the existing user's token.

Code in my Startup.cs class:

services
    .AddMicrosoftIdentityWebApiAuthentication(Configuration, "ApiSettings")
    .EnableTokenAcquisitionToCallDownstreamApi()//This does the magic
    .AddInMemoryTokenCaches();

In my API controller, I had to inject ITokenAcquisition using something like:

private readonly ITokenAcquisition _tokenAcquisition;

public MyController(ITokenAcquisition tokenAcquisition)
{
    _tokenAcquisition = tokenAcquisition;
}

and then using this to acquire a token for Azure Management API:

/// <summary>
/// Gets the access token on behalf of signed-in user to perform Azure
/// Resource Manager (ARM) API.
/// </summary>
/// <returns>
/// Access token.
/// </returns>
private async Task<string> GetAccessTokenForAzureSubscriptionManagementApiRequest()
{
    IEnumerable<string> azureServiceManagementApiScopes = <scopes for Azure Management;
    string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(azureServiceManagementApiScopes);
    return accessToken;
}

You can learn more about it here: https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-call-api-app-configuration?tabs=aspnetcore.

Upvotes: 4

Related Questions