Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Bearer error="invalid_token", error_description="The issuer '(null)' is invalid" in v1.14.1 #1324

Closed
1 of 8 tasks
throck95 opened this issue Jul 19, 2021 · 18 comments
Closed
1 of 8 tasks
Labels

Comments

@throck95
Copy link

throck95 commented Jul 19, 2021

Which version of Microsoft Identity Web are you using?
v1.14.1

Where is the issue?

  • Web app
    • Sign-in users
    • Sign-in users and call web APIs
  • Web API
    • Protected web APIs (validating tokens)
    • Protected web APIs (validating scopes)
    • Protected web APIs call downstream web APIs
  • Token cache serialization
    • In-memory caches
    • Session caches
    • Distributed caches
  • Other (please describe)

Is this a new or an existing app?
This is an app under active development and live in a production system for which I have successfully used v1.12.0.

Repro
Similar to Thomas Barnekow in #1310, I have made no code changes within my application. I branched from main and updated from v1.12.0 to v1.14.1. Following this, the API starts failing to validate tokens generated by Azure AD via MSAL.

Server side, I am using .NET 5 with the following configuration:

appsettings.json

"AzureAdB2C": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "[tenant_guid]",
    "Domain": "gravitydiagnosticsb2c.onmicrosoft.com",
    "ClientId": "[guid]",
    "AllowWebApiToBeAuthorizedByACL": true

My API utilizes the token for authentication and then routes authentication through a database for role assignments. As such, the ACL bypass is needed.

Startup.ConfigureServices(IServiceCollection services)

var tenantId = Configuration.GetSection("AzureAdB2C:TenantId").Value;
var metadataAddress = $"https://login.microsoftonline.com/{tenantId}/.well-known/openid-configuration";
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
     .AddMicrosoftIdentityWebApi(options =>
     {
          Configuration.Bind("AzureAdB2C", options);

          options.TokenValidationParameters.NameClaimType = "name";
          options.MetadataAddress = metadataAddress;
     },
     options => { Configuration.Bind("AzureAdB2C", options); });

Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
     if (env.IsDevelopment())
     {
          app.UseDeveloperExceptionPage();
     }

     //Custom middleware to configure logging to various ancillary systems based on configuration content and values
     app.UseConfigureLoggerMiddleware(Configuration);

     app.UseStaticFiles();
     app.UseSwagger();
     app.UseSwaggerUI(c =>
     {
          foreach (var description in provider.ApiVersionDescriptions)
          {
               c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
          }
     });

     app.UseHttpsRedirection();

     app.UseRouting();

     app.UseAuthentication();
     app.UseAuthorization();

     app.UseEndpoints(endpoints =>
     {
          endpoints.MapControllers();
     });
}

Below is my decoded and validated token retrieved from jwt.ms:

{
  "aud": "[guid1]",
  "iss": "https://login.microsoftonline.com/[tenant_guid]/v2.0",
  "iat": 1626712696,
  "nbf": 1626712696,
  "exp": 1626716596,
  "aio": "[removed]",
  "azp": "[guid2]",
  "azpacr": "1",
  "oid": "[guid3]",
  "rh": "[removed]",
  "sub": "[guid3]",
  "tid": "[tenant_guid]",
  "uti": "[guid4]",
  "ver": "2.0"
}

Similar to previous reports with v1.13.0 and v1.14.0, the iss claim is not null and the manifest is issuing a v2.0 token.

Expected behavior
Token validation works as in v1.12.0 and no error is returned.

Actual behavior
With v1.13.0 through v1.14.1, the Web API only returns error responses with status code 401 Unauthorized and a WWW-Authenticate header with a value of Bearer error="invalid_token", error_description="The issuer '(null)' is invalid".

Logs

2021-07-19 12:51:38.034 -04:00 [INF] Failed to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDW10303: Issuer: 'https://login.microsoftonline.com/[tenant_guid]/v2.0', does not match any of the valid issuers provided for this application. 
   at Microsoft.Identity.Web.Resource.AadIssuerValidator.Validate(String actualIssuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateIssuer(String issuer, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
2021-07-19 12:51:38.375 -04:00 [INF] Bearer was not authenticated. Failure message: IDW10303: Issuer: 'https://login.microsoftonline.com/[tenant_guid]/v2.0', does not match any of the valid issuers provided for this application. 
2021-07-19 12:51:38.442 -04:00 [INF] Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
2021-07-19 12:51:38.490 -04:00 [INF] AuthenticationScheme: Bearer was challenged.
@jmprieur
Copy link
Collaborator

@throck95 : can you please enable PII to see the issuer displayed in the error message
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/PII

This is not B2C, btw? It's AAD with a B2C tenant?

@throck95
Copy link
Author

@jmprieur The issuer returned in the error message is there. The [guid] value is the tenant guid of the host. As for your second question, yes we're using B2C here and we're using the AAD B2C to authenticate both organizational users and external users to access our system. Sometimes we create an app registration and generate a secret. Other times, it's pass-thru authentication from an MVC.

@jmprieur
Copy link
Collaborator

@throck95 : I'm not seeing that your configuration is B2C because:

Would you mind distiguishing guid into guid1 and guid2 ?

@throck95
Copy link
Author

@jmprieur I've updated the guids to separate them out based on their respective values.

Below you'll find the screenshot where we retrieve an access token and authenticate against the API when running v1.14.1. This results in the aforementioned error.
image

Below is an image of the exact same request using v1.12.0 with no system changes whatsoever. This results in the expected response where we access application code.
image

If I understand you're second point correctly, the instance specification is incorrect and the API should be rejecting tokens altogether. I can certainly see this as plausible, however, the above scenario shows that on the last working version it was operational with the invalid instance. So I'm not sure where to go from here...

@throck95
Copy link
Author

Is there any additional information I can provide to assist with the research into why v1.14.1 would still be returning a bearer error still?

@jmprieur
Copy link
Collaborator

@throck95 : why do you provider options.MetadataAddress = metadataAddress; ?
can you please remove this and check?

@throck95
Copy link
Author

@jmprieur That was in there as a result of my using the Instance of login.microsoftonline.com. Even using /tfp this was still required as it had to do with the authority being issued on the bearer token (https://github.com/AzureAD/microsoft-identity-web/wiki/Azure-AD-B2C-issuer-claim-support). I've changed the Instance in the appSettings now to:

"AzureAdB2C": {
    "Instance": "https://gravitydiagnosticsb2c.b2clogin.com/",
    "TenantId": "[tenant_guid]",
    "Domain": "gravitydiagnosticsb2c.onmicrosoft.com",
    "ClientId": "[client_guid]",
    "AllowWebApiToBeAuthorizedByACL": true
  }

This change allows the MetadataAddress to not be needed. However, it still results in the same behavior outlined in the screenshots above. v1.14.1 returns a 401 with the same www-authenticate message:

image

@jmprieur
Copy link
Collaborator

jmprieur commented Jul 22, 2021

@throck95 : you are missing policies

"AzureAdB2C": {
"Instance": "https://fabrikamb2c.b2clogin.com",
"ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
"Domain": "fabrikamb2c.onmicrosoft.com",
"SignedOutCallbackPath": "/signout/B2C_1_susi",
"SignUpSignInPolicyId": "b2c_1_susi",
"ResetPasswordPolicyId": "b2c_1_reset",
"EditProfilePolicyId": "b2c_1_edit_profile" // Optional profile editing policy

@throck95
Copy link
Author

@jmprieur I've got policies in my appsettings. I just didn't think they were relevant to list out. My apologies.

"AzureAdB2C": {
    "Instance": "https://gravitydiagnosticsb2c.b2clogin.com/",
    "TenantId": "[tenant_guid]",
    "Domain": "gravitydiagnosticsb2c.onmicrosoft.com",
    "ClientId": "[client_guid]",
    "SignedOutCallbackPath": "/signout/B2C_1_[appname]_User_Sign_Up_Sign_In",
    "SignUpSignInPolicyId": "B2C_1_[appname]_User_Sign_Up_Sign_In",
    "ResetPasswordPolicyId": "B2C_1_[appname]_PW_Reset",
    "EditProfilePolicyId": "B2C_1_[appname]_Profile_Edit",
    "AllowWebApiToBeAuthorizedByACL": true
  }

@jmprieur jmprieur added answered question Further information is requested and removed investigate labels Jul 22, 2021
@throck95
Copy link
Author

@jmprieur Please let me know if there is any additional information you need me to provide.

@jennyf19
Copy link
Collaborator

jennyf19 commented Aug 9, 2021

@throck95 do you see this with the latest Id web version? 1.15.2
also, can you provide verbose logs with PII if possible so we can see the values? you can email the logs if you prefer -> jeferrie@microsoft.com

@throck95
Copy link
Author

throck95 commented Aug 9, 2021

@jennyf19 This issue is still occurring with the latest 1.15.2 version. The logs provided in the original post (minus the tenant guids) are verbose logging. Due the authentication issue, the API won't pass the authorization handling and proceed to any application logic.

@jennyf19
Copy link
Collaborator

@throck95 can you point us to some repro code? thanks.

@throck95
Copy link
Author

@jennyf19 In my original request I provided copies of the components of my Startup that configure the authentication. Is there anything specific you're looking that is not provided there? The issue is all happening in the authentication middleware so actual business / application logic is not being executed.

@jmprieur
Copy link
Collaborator

@throck95 there were iterations, between not needing the Metadata address, the authority which wasn't a b2c one, the lack of policy. It would be useful to get a refresh of your startup.cs and appsettings.json ...

@throck95
Copy link
Author

Below find the most up-to-date copies of the relevant code.

Startup.ConfigureServices(IServiceCollection services)

public void ConfigureServices(IServiceCollection services)
        {
            //Setup B2C Authentication
            var tenantId = Configuration.GetSection("AzureAdB2C:TenantId").Value;
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApi(options =>
                {
                    Configuration.Bind("AzureAdB2C", options);

                    options.TokenValidationParameters.NameClaimType = "name";
                },
                options => { Configuration.Bind("AzureAdB2C", options); });

            services.AddControllers();

            services.AddDbContext<DbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("AzureZodiac"),
                sqlServerOptionsAction: sqlOptions =>
                {
                    sqlOptions.EnableRetryOnFailure(10, TimeSpan.FromSeconds(5), null);
                }));

            //Add Business Layer Services
            services.AddTransient(typeof(Service1));
            services.AddTransient(typeof(Service2));
            services.AddTransient(typeof(Service3));

            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(Configuration)
                .CreateLogger();
            services.AddSingleton(Log.Logger);

            //Add Swagger Specifications
            services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
            services.AddSwaggerGen(options =>
            {
                options.OperationFilter<SwaggerDefaultValues>();

                options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    In = ParameterLocation.Header,
                    Description = "Please enter Access Token with Bearer into field.",
                    Name = "Authorization",
                    Type = SecuritySchemeType.ApiKey
                });
                options.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    { 
                        new OpenApiSecurityScheme 
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                        },
                        Array.Empty<string>()
                    }
                });

                options.IncludeXmlComments(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), XmlCommentsFileName));
            });

            //Setup API to use URL Versioning
            services.AddApiVersioning(options =>
            {
                options.DefaultApiVersion = new ApiVersion(1, 0);
                options.AssumeDefaultVersionWhenUnspecified = true;
                options.ApiVersionReader = new UrlSegmentApiVersionReader();
                options.ReportApiVersions = true;
            });
            services.AddVersionedApiExplorer(options =>
            {
                // NOTE: the specified format code will format the version as "'v'major[.minor][-status]"
                options.GroupNameFormat = "'v'VVV";
                options.SubstituteApiVersionInUrl = true;
            });

            //Configure Failed Request Middleware
            services.AddMvc()
                .ConfigureApiBehaviorOptions(options =>
                {
                    options.InvalidModelStateResponseFactory = context =>
                    {
                        var problems = new CustomBadRequest(context, Log.Logger);
                        return new BadRequestObjectResult(problems);
                    };
                });
        }

Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                foreach (var description in provider.ApiVersionDescriptions)
                {
                    c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
                }
            });

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

appsettings.json

  "AzureAdB2C": {
    "Instance": "https://gravitydiagnosticsb2c.b2clogin.com/",
    "TenantId": "[tenant_guid]",
    "Domain": "gravitydiagnosticsb2c.onmicrosoft.com",
    "ClientId": "[guid]",
    "SignedOutCallbackPath": "/signout/B2C_Policy_User_Sign_Up_Sign_In",
    "SignUpSignInPolicyId": "B2C_Policy_User_Sign_Up_Sign_In",
    "ResetPasswordPolicyId": "B2C_Policy_PW_Reset",
    "EditProfilePolicyId": "B2C_Policy_Profile_Edit",
    "AllowWebApiToBeAuthorizedByACL": true
  }

@throck95
Copy link
Author

@jmprieur Please let me know if the above information is not enough or you need additional details.

@jennyf19
Copy link
Collaborator

@throck95 Does this repro with the latest Id. Web? We've fixed the AadIssuerValidator, which we now pull from Microsoft.IdentityModel.Validators.

Please re-open if the issue persists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants