question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Bug] Protecting WebAPI does not work if one wants to use multiple bearer schemes, e.g. for AAD *and* B2C

See original GitHub issue

Using Microsoft Identity Web 0.2.3-preview protecting WebAPI with multiple Bearer schemes for different authorities.

Referring to this sample for multi bearer auth for Web Apis, it is impossible to use Microsoft.Identity.Web package to accomplish the required setup.

Steps to reproduce:

Create the following configuration file (only relevant sections are listed here for clarity):

{
  "AzureAdB2C": {
    "Instance": "https://snetga.b2clogin.com/",
    "ClientId": "3422b1f4-8039-4023-9373-dbe747b40125",
    "TenantId": "126bbbd5-e975-43a7-88ac-babc84279788",
    "Domain": "snetga.onmicrosoft.com",
    "SignUpSignInPolicyId": "B2C_1A_signup_signin_google"
  },
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "126bbbd5-e975-43a7-88ac-babc84279788",
    "ClientId": "3422b1f4-8039-4023-9373-dbe747b40125"
  }
}

Then try to register both AzureAd and AzureAdB2C the following way:

            services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C", "B2C");
            services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd", "v2.0");

            services.AddAuthorization(options =>
            {
                options.DefaultPolicy = new AuthorizationPolicyBuilder()
                    .RequireClaim("iss")
                    .AddAuthenticationSchemes("B2C", "v2.0")
                    .Build();
            }
            );

If the order is like here displayed - first registering AzureAdB2C and then registering AzureAd, you will get the following error when calling the API with valid token:

System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://login.microsoftonline.com/snetga.onmicrosoft.com/B2C_1A_signup_signin_google/v2.0/.well-known/openid-configuration'.
 ---> System.IO.IOException: IDX20807: Unable to retrieve document from: 'https://login.microsoftonline.com/snetga.onmicrosoft.com/B2C_1A_signup_signin_google/v2.0/.well-known/openid-configuration'. HttpResponseMessage: 'StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{

If changing the order - first register the AzureAd and then AzureAdB2C, e.g.:

            services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd", "v2.0");
            services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C", "B2C");
            ...

You will get success (200) calling the WebAPI with Azure AD B2C token, but 401 calling the API using v2.0 Token.

If you only use one of them (either AzureAd only or AzureAdB2C only) - it works.

Also, if you use the Microsoft.Identity.Web extensions to register only one of the providers (AzureAd OR AzureAdB2C), but use the Microsoft.AspNetCore.Authentication.XXX for the other provider, e.g.:

          services.AddAuthentication()
            .AddJwtBearer("v2.0", options =>
            {
                options.Authority = $"{Configuration["AzureAd:Instance"]}/{Configuration["AzureAd:TenantId"]}/v2.0";
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = $"{Configuration["AzureAd:Instance"]}/{Configuration["AzureAd:TenantId"]}/v2.0",
                    ValidateAudience = true,
                    ValidAudience = Configuration["AzureAd:ClientId"],
                    ValidateLifetime = true
                };
            }); services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAd", "v2.0");
            
            services.AddMicrosoftWebApiAuthentication(Configuration, "AzureAdB2C", "B2C");


            services.AddAuthorization(options =>
            {
                options.DefaultPolicy = new AuthorizationPolicyBuilder()
                    .RequireClaim("iss")
                    .AddAuthenticationSchemes("B2C", "v2.0")
                    .Build();
            }
            );

Then the API accepts both AzureAd (v2.0) AND AzureAdB2C tokens.

After some very short search through the code, it seems this registration in WebApiAuthenticationBuilderExtensions might be causing the issue. Because of builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions<MicrosoftIdentityOptions>, MicrosoftIdentityOptionsValidation>()), which is trying to add a singleton of MicrosoftIdentityOptions

  • basically the second use of AddMicrosoftWebApiAuthentication will override any configuration settings that are different from the first registration. If I read correctly the documentation about TryAddEnumerable, it explains the behavior I am observing. But this is just a guess.

Expected behavior is that I am able to use AddMicrosoftWebApiAuthentication multiple times with different configuration settings. There are a lot of valid scenarios where one would like to have this:

  • Using Azure AD B2C for end-users and client credentials flow (which is exactly what this sample is about)
  • Having an application that authenticates users from different authorities - e.g. Commercial and China/GCC etc.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
pmaytakcommented, Aug 18, 2020

Thanks @astaykov for investigation and a write-up.

This is also a duplicate of #429. I wrote a reply there.

The issue is with calling builder.Services.Configure(configureMicrosoftIdentityOptions); twice - with second time overwriting the first options.

builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions<MicrosoftIdentityOptions>, MicrosoftIdentityOptionsValidation>()); I think is fine because it adds a singleton of a validator class, not options class.

0reactions
jennyf19commented, Sep 11, 2020
Read more comments on GitHub >

github_iconTop Results From Across the Web

Enable authentication in a web API by using Azure Active ...
This article shows you how to enable Azure AD B2C authorization to your web API. After you complete the steps in this article,...
Read more >
Multiple authentication schemes: Azure AD and ...
I need to use two authentication schemes in my app: 1.) Azure AD B2C user login. 2.) Azure AD machine-to-machine daemon login.
Read more >
c# - Use multiple JWT Bearer Authentication
The blog post suggests to use seperate handlers for each scheme, that doesn't seem to be needed, a generic class JWTAuthenticationHandler that ...
Read more >
Integration Scenarios Between Azure API Management and ...
In this video, we explore all the different scenarios for integrating Azure API Management with an OAuth2 IdP such as Azure AD B2C....
Read more >
Secure a .NET Core API with Bearer Authentication - YouTube
NET Core API with JWT Bearer Authentication, using Azure Active Directory as ... For example a user might have a profile in our...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found