Search code examples
asp.netasp.net-corejwtasp.net-identity

How to include claim on Identity Roles in JWT?


I have implemented Authentication and Authorization in my Asp.Net core Web API application following templates and instructions by Microsoft (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0).

In the authentication JWT token I would like to include a claim with the Identity Roles of the user, so that I can easily hide unnecessary modules in my SPA client application and for quick authorization on the API controllers. However the default JWT does not include claims on the Roles.

In my currently used middleware, can I configure the JWT token to include the roles claim? Note I have not created any definition for the current token, the middleware automatically created the tokens for me.

Edit I have changed the configuration, so that the role is a claim on the user. In addition my configuration has changed so that the scope includes this claim. The resulting token still does not include the roles.

My main application hosts both the Web API and the authentication server. Packages:

<PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" />

Startup:

...
services.AddDefaultIdentity<AppUser>()
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<DbContext>();

services.AddAuthentication()
        .AddIdentityServerJwt();

services.AddIdentityServer()
    .AddApiAuthorization<PimUser, PimDbContext>(options =>
    {
        options.IdentityResources.Add(new IdentityResource(
            name: "roles",
            displayName: "Roles",
            claimTypes: new List<string>{"role"}) { Required = true});
        options.Clients.AddSPA("authAngular", spa =>
            spa.WithScopes("AppAPI", "openid", "profile", "roles")
                .WithClientId("pimAngular")
                .WithRedirectUri("https://localhost:5001/authentication/login-callback")
                .WithLogoutRedirectUri("https://localhost:5001/authentication/logout-callback"));
    });
...

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

Users are added to role and a claim is added at the same time:

await userManager.AddToRolesAsync(user, role);
await userManager.AddClaimAsync(user, new Claim("role", role));

The configuration of openid does include the new scope:

"scopes_supported": [
    "openid",
    "profile",
    "roles",
    "AppAPI",
    ...
],
"claims_supported": [
    ...
    "role"
]

And when I query the configuration for authClient I also get the correct scope:

{
  "authority": "https://localhost:5001",
  "client_id": "authAngular",
  "redirect_uri": "https://localhost:5001/authentication/login-callback",
  "post_logout_redirect_uri": "https://localhost:5001/authentication/logout-callback",
  "response_type": "code",
  "scope": "AppAPI openid profile roles"
}

The resulting token still doesn't include the roles claim. What am I missing?


Solution

  • The edited configuration does work, but only after creating a new database and deleting all previous migrations. There must have been some corrupt data/scheme messing it up for me.