Search code examples
asp.net-mvcasp.net-identityidentityserver4

roles not being populated by AddJWTBearer using IdentityServer4 and dotnetcore 2.0


I am using these assemblies with the below configuration:

  • IdentityServer4" Version="2.0.0-rc1
  • IdentityServer4.AspNetIdentity" Version="2.0.0-rc1

My request for a token at connect/token gives me a correct bearer token, and when I call a method using [Authorize(JwtBearerDefaults.AuthenticationScheme)] with the token, the authorisation seems to work correctly.

However, the roles are blank / empty?
How do I get the token request to include the necessary ASPNET roles?

With the following configuration

services.AddAuthentication()
        .AddOpenIdConnect(
        o =>
        {
            o.Authority = "https://localhost:44319";
            o.ClientId = "api";
            o.ClientSecret = "secret";
            o.RequireHttpsMetadata = false;
            o.GetClaimsFromUserInfoEndpoint = true;
            o.TokenValidationParameters = new TokenValidationParameters
            {
                RoleClaimType = ClaimTypes.Role
            };
        })
        .AddJwtBearer(o =>
            {
                o.Authority = "https://localhost:44319";
                o.Audience = "api";
                o.RequireHttpsMetadata = false;
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    RoleClaimType = ClaimTypes.Role
                };
                o.SaveToken = true;
            });

services.AddMemoryCache();
services.AddIdentity<ApplicationUser, ApplicationRole>(
        x =>
        {
            x.Password.RequireNonAlphanumeric = false;
            x.Password.RequireUppercase = false;
        })
    .AddEntityFrameworkStores<FormWorkxContext>()
    .AddDefaultTokenProviders()
    .AddIdentityServer();

services.ConfigureApplicationCookie(options =>
{
    options.LoginPath = "/login";
    options.LogoutPath = "/logout";
    options.Events.OnRedirectToLogin = this.ProcessStatusCodeResponse;
});

services.AddIdentityServer()
    // .AddSigningCredential("CN=rizacert")
    .AddDeveloperSigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApis())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>();

and config.cs

private const string Api = "api";
private const string ClientSecret = "secret";

public static IEnumerable<ApiResource> GetApis()
{
    return new List<ApiResource>
    {
        new ApiResource(Api, "formworkx api")
    };
}

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client
        {
            ClientId = "api",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
            RequireConsent = false,
            ClientSecrets = { new Secret(ClientSecret.Sha256()) },
            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.OfflineAccess,
                "api"
            }
        }
    };
}

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    };
}

Solution

  • The claim types need to be requested in the ApiResource.

    public static IEnumerable<ApiResource> GetApis()
    {
        return new List<ApiResource>
        {
            new ApiResource(
                Api,
                "formworkx api",
                new[]
                {
                    // exhaustive list of claims in a new
                    // dotnetcore 2.0 MVC application.
                    "nbf",
                    "exp",
                    "iss",
                    "aud",
                    "aud",
                    "client_id",
                    "sub",
                    "auth_time",
                    "idp",
                    "AspNet.Identity.SecurityStamp",
                    ClaimTypes.Role,  // REQUESTED HERE
                    "preferred_username",
                    "name",
                    "email",
                    "email_verified",
                    "scope",
                    "amr"
                })
        };
    }
    

    Additionally, the role type needs to be correct.

    services.AddIdentity<ApplicationUser, ApplicationRole>(
       x =>
       {
           x.Password.RequireNonAlphanumeric = false;
           x.Password.RequireUppercase = false;
       })
       .AddEntityFrameworkStores<FormWorkxContext>()
       .AddDefaultTokenProviders()
       .AddIdentityServer();
    
       // NB (hours of debugging..., AddIdentityServer uses "role")
       services.Configure<IdentityOptions>(options => 
           options.ClaimsIdentity.RoleClaimType = ClaimTypes.Role);