简体   繁体   中英

How to deal with multiple APIs with Azure B2C

I need assistance to understand the how Azure B2C can work in case if multiple APIs are required.

We have in this sample: https://api01.azurefunction.com/ https://api02.azurefunction.com/

https://app.azuresites.com/

B2C Instance samples.onmicrosoft.com

API01 exposes scope: https://samples.onmicrosoft.com/api01/read

API02 expose scope: https://samples.onmicrosoft.com/api02/write

WebApp uses MSAL.NET (Microsoft.Identity.Client 4.9.0) and can request scopes ONLY for one API. Any attempts to request scopes for two different resources will fail, it's limitation in AD/B2C in case if used ConfidentialClientApplicationBuilder.

My attempts initiate multiple round trips to STS by requesting scopes were failed. Even if it would work, it cannot be considered for production use due to possibility of showing consent screen.

In that point I need advice how to work in current case.

I have multiple options:

  1. Implement facade for these API and provide single point to request data. But this has drawbacks at least of consuming resources. Other significant limitation it's On-Behalf-Of not supported in B2C. Therefore I need to invent how to call underling Azure Functions with incoming security context.

  2. Use API Management instance with AD integration but price so big for our project (~2K/month)

  3. Use a "hack" to define "Allowed Token audiences" for AD protected Azure Function to allow accept tokens acquired for one resource but sent to another. This work but I will be limited in scopes because list of scopes will be defined by the resource to which token was acquired. As a variation I can register "fake" application and always acquire tokens to that application.

  4. Implement custom AccessToken binders in Azure Function and completely invent a wheel:(

Option #3 looks good. Has some limitations but I hope I can deal with that.

But in final, in case if I will need communicate with Azure APIs such as AD Graph API how to get a token? Use RBAC?

Please assist with this.

In this case where you use web application, it is recommended to use Microsoft.Identity.Web library instead of MSAL.NET.

Here is my configuration in the Blazor Server application (in the Program.cs file):

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration, "AzureAdB2CConfiguration")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

As you can see above, I am using EnableTokenAcquisitionToCallDownstreamApi() method which will enable obtaining access tokens to call multiple APIs.

Also, as you can see there is AddInMemoryTokenCaches() method which will store access tokens in the memory. For the production scenarios you should use some external storage like Redis, CosmosDB, or SQL db. You can read more about these options here .

Now in you web application code, you get access to ITokenAcquisition interface implementation which enables you to obtain the access tokens for multiple APIs. Here is one of the services I have in my web app to call API:

internal class UserManagementApiService
 {
    private readonly HttpClient _httpClient;
    protected readonly ITokenAcquisition _tokenAcquisition;
    private readonly IUserManagementApiConfiguration _userManagementApiConfiguration;

public UserManagementApiService(HttpClient httpClient,
            ITokenAcquisition tokenAcquisition,
            IUserManagementApiConfiguration userManagementApiConfiguration)
{
    _httpClient = httpClient;
    _tokenAcquisition = tokenAcquisition;
    _userManagementApiConfiguration = userManagementApiConfiguration;
}

public async Task<HttpResponseMessage> CreateUserAsync(NewUserAccount userAccount)
{
    await GetAndAddApiAccessTokenToAuthorizationHeaderAsync();
    var request = new HttpRequestMessage()
    {
        Method = HttpMethod.Post,
        RequestUri = new Uri($"{_userManagementApiConfiguration.Url}/new-user")
    };
    request.Content = new StringContent(JsonSerializer.Serialize(userAccount), Encoding.UTF8, "application/json");
    var response = await _httpClient.SendAsync(request);

    return response;
}


protected async Task GetAndAddApiAccessTokenToAuthorizationHeaderAsync()
{
    string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { _userManagementApiConfiguration.Scope },
                                                                            authenticationScheme: OpenIdConnectDefaults.AuthenticationScheme);
    _httpClient.DefaultRequestHeaders.Authorization
                                      = new AuthenticationHeaderValue("Bearer", accessToken);

}

As you can see above, I am using _tokenAcquisition.GetAccessTokenForUserAsync() method to get access token for specific API. As a parameter I have to provide specific API scope.

Please also remember that you have to grant permissions to your API from the web app in the Azure AD B2C portal for your registered web app, under the API permissions tab:

在此处输入图像描述

With this approach, you can request access tokens for multiple APs. It is impossible to obtain multiple access tokens at once, in the one request. You always obtain one access token for a specific resource basing on the scope you add in the request to the AD B2C.

With this approach you can also make sure that you implement granular authorization and not mixing the audience parameter between different APIs.

I hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM