I am maintaining an ASP.NET Core Web API project, which uses Duende Identity Server with Jwt bearer tokens, and role-based access to endpoints.
After moving the project to .NET 8 (upgrading the Microsoft.AspNetCore.App
framework to version 8.0.7), I started getting http 403 errors.
Here is my Identity set up in builder.ConfigureServices()
:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("Bearer", options =>
{
options.Authority = "id.YouDontNeedToKnow.example.com";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
NameClaimType = "sub",
RoleClaimType = "role",
ValidTypes = [ "at+jwt" ]
};
});
builder.Services
.AddAuthorization(options =>
{
options.AddPolicy("Admin",
AuthorisationPolicies.Admin());
});
Tracing an endpoint hit from Swagger, I found the following failure:
RolesAuthorizationRequirement:User.IsInRole must be true for one of the following roles: (Admin)
Something is wrong with the way it reads roles from my Jwt.
This announcement describes why I am having the problem. A breaking change in Asp.Net Core v8 changes default real implementation of TokenValidatedContext.SecurityToken from JwtSecurityToken to JsonWebToken.
One effect of this is that using JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear()
to prevent inbound claim type mapping is no longer effective.
I had a claim with type "Role" which used to contain the data which matched the Authorization Policy listed in the code contained in my question. That claim still exists in the Bearer token as sent by the site, but on contact with the Asp.Net Core middleware it is changed to a claim with the standard value for ClaimTypes.Role
: "http://schemas.microsoft.com/ws/2008/06/identity/claims/role".
At this point I decided to bend with the wind, and remove the line:
RoleClaimType = "role",
...as it was causing a mismatch, so now I am using the standard claim type to identify Role claims. Removing that line resolves the problem and standardises my implementation.