Reputation: 1449
I followed Microsofts article to implement my own issuer validation ("Customizing token validation" is the headline of the section).
This seemed to work with JWT-Tokens issued in an app-only context, but failed when the first call to my API was a JWT token issued through user delegation.
I found that this line of code is causing the problem:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration);
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingOnTokenValidatedHandler(context);
// Your code to add extra configuration that will be executed after the current event implementation.
options.TokenValidationParameters.ValidIssuers = new[] { /* list of valid issuers */ };
options.TokenValidationParameters.ValidAudiences = new[] { /* list of valid audiences */};
}
});
This is the original code from the link I posted above. I implemented my own issuer validation in the following way:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.TokenValidtionParameters.RoleClaimType = "roles";
options.Events.OnTokenValidated = async context =>
{
await existingOnTokenValidatedHandler(context);
options.Authority = "https://login.microsoftonline.com/common";
var validTenants = FileTenantStore.Tenants.Select(x => x.AzureAdTenantId).ToList();
options.TokenValidationParameters.ValidIssuers = GetValidIssuers(validTenants);
options.TokenValidationParameters.IssuerValidator = ValidateIssuers;
};
});
I have a multi tenant app, so I need to let only some tenants pass and reject the most.
This solution behaves a little strange:
Failed to validate the token. Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDW10303: Issuer: 'https://login.microsoftonline.com/{OUR_TENANT_ID}/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()
So in this case "OnTokenValidated" is never called.
I could fix this problem with moving the lines in the "OnTokenValidated"-Callback one level above:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.TokenValidationParameters.RoleClaimType = "roles";
var validTenants = FileTenantStore.Tenants.Select(x => x.AzureAdTenantId).ToList();
options.TokenValidationParameters.ValidIssuers = GetValidIssuers(validTenants);
options.TokenValidationParameters.IssuerValidator = ValidateIssuers;
options.Events.OnTokenValidated = async context =>
{
await existingOnTokenValidatedHandler(context);
options.Authority = "https://login.microsoftonline.com/common";
};
});
I could even remove the callback "OnTokenValidated" now, but this does not feel right, because of the Microsoft article that is giving clear instructions.
Can I do it like this, or is my solution a security problem?
Upvotes: 1
Views: 3436
Reputation: 985
If you are in netcore>3 could you use AddMicrosoftIdentityWebApiAuthentication extension method. Set subscribeToOpenIdConnectMiddlewareDiagnosticsEvents to true and enable debug logging to look at the event that terminate the check for your particular token.
Based on your code :
services.AddMicrosoftIdentityWebApiAuthentication(Configuration,
jwtBearerScheme: JwtBearerDefaults.AuthenticationScheme,
subscribeToJwtBearerMiddlewareDiagnosticsEvents: true);
Upvotes: 1