Search code examples
c#identityserver4openid-connectrolesclaims

IdentityServer4 role based authorization correct use


I'm currently working on a .net5.0 IdentityServer4 application.

I want to use role based authorization, unfortunately it seems not to work correctly.

When I use the [Authorization] attribute on my controller action, I can logon correctly.

When I use [Authorize(Roles = "admin")] attribute on my controller action, I get forwarded to the access denied page (403).

When I login with my TestUser with role admin, I get forwarded to the access denied page on my client app.

My application structure looks like this:

  • IdentityServer4
  • OIDC Client (Wen App, Authorization Code + PKCE)

The IdentityServer4 Configuration looks like this:

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client
        {
            ClientId = "oidcClient",
            ClientName = "Example Client Application",
            ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) },

            AllowedGrantTypes = GrantTypes.Code,
            RedirectUris = new List<string> { "https://localhost:xxxx/signin-oidc" },
            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.Email,
                "role"
            },

            RequirePkce = true,
            AllowPlainTextPkce = false
        }
    };

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new[]
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
        new IdentityResources.Email(),
        new IdentityResource
        {
            Name = "role",
            UserClaims = new List<string> { "role" }
        }
    };
}

public static List<TestUser> GetUsers()
{
    return new List<TestUser> {
        new TestUser {
            SubjectId = "123ABC",
            Username = "admin",
            Password = "password",
            Claims = new List<Claim> {
                new Claim(ClaimTypes.Email, "[email protected]"),
                new Claim(ClaimTypes.Role, "admin")
            }
        }
    };
}

The Oidc connect configuration on my client app looks like this:

services.AddAuthentication(options => 
{
    options.DefaultScheme = "cookie";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("cookie")
.AddOpenIdConnect("oidc", options => 
{
    options.Authority = "https://localhost:5000";
    options.ClientId = "oidcClient";
    options.ClientSecret = "secret";

    options.ResponseType = "code";
    options.UsePkce = true;
    options.ResponseMode = "query";

    // Edit 1
    options.TokenValidationParameters = new TokenValidationParameters
    {
       NameClaimType = ClaimTypes.Name,
       RoleClaimType = ClaimTypes.Role
    };

    options.SaveTokens = true;
});

My controller action is quite simple - only roles no policies yet - and looks like this:

[Authorize(Roles = "admin")] // Roles does not work
//[Authorize] works fine
public IActionResult Home()
{
    return View();
}

It feels like the access token is not passed to my client - only the idenity token...

Anyways, when I debug the action the User claims look like this: enter image description here

I use an IdentityServer4 TestUser and I don't have an IProfileService implementation yet. Is it necessary?

Do you know how to solve this issue?

How can I use the role claim in my application correctly?


Solution

  • IdentityServer/OpenIDConnect and Microsoft have different opinions on what the name claim should be be called. So you need to tell your app what your role and name claim is called.

    Like this:

    }).AddOpenIdConnect(options =>
    {
        ...
        
        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = JwtClaimTypes.Name,
            RoleClaimType = JwtClaimTypes.Role,
        };
    
    });