After upgrading from ASP.NET Core 3.1 to version 5, context.User.Claims
is empty in
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
in
public class MyRequirementHandler : AuthorizationHandler<MyRequirement>
I'm using the Authorization
header with a bearer token with JWT. I can see that header being set correctly when looking at the HttpContext.Request.Headers
but it doesn't seem to be parsed.
This is set up on a Grpc Service with the [Authorize]
attribute.
With ASP.NET Core 3.1, it worked fine. I went through the official migration guide but their references regarding authorisation were only for Azure Active Directory.
I'm using IdentityServer4 which is hosted within that ASP.NET Core app as a middleware (app.UseIdentityServer();
)
What did I forget to modify to get ASP.NET Core to parse the authorisation header correctly?
Update:
I checked it in more details and noticed that it's failing because it can't verify the audience (aud
) - and yes on the newly created tokens the audience is missing (the old tokens had the audience). Also I noticed that a custom scope, which I was adding in
public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
inside my custom
public class ProfileService : ProfileService<ApplicationUser>
is also missing after the update. This is how the IdentityServer is configured:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppIdentityDbContext>()
.AddProfileService<ProfileService>()
.AddInMemoryIdentityResources(AuthResources.GetIdentityResources())
.AddInMemoryApiResources(AuthResources.GetApiResources())
.AddInMemoryClients(TestClientsRequired
? ClientsForTesting.GetTestClients()
: Clients.GetDefaultClients());
After figuring out that the issue might have been due to a missing audience (aud
) I looked further and found Missing "aud" claim in access token - the answer was, to explicitly add the audience as a claim and also set the scope one more time, and it worked.
For me this looks the following way:
public static IEnumerable<ApiResource> GetApiResources()
{
yield return ApiResourceBuilder
.IdentityServerJwt(MyWebApiResource)
.AllowAllClients()
.Build()
.AddUserClaims()
.AddScopes(); // <- this is new
}
private static T AddUserClaims<T>(this T resource)
where T : Resource
{
resource.UserClaims.Add(Constants.CustomClaimTypes.MyRoles);
resource.UserClaims.Add(JwtClaimTypes.Audience); // <- this is new
return resource;
}
// this whole method is new ->
private static T AddScopes<T>(this T resource)
where T : ApiResource
{
resource.Scopes.Add(MyWebApiResource);
return resource;
}