question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[JWT] TokenValidationParameters RoleClaimType Not Fully Respected

See original GitHub issue

Original issue: https://github.com/aspnet/AspNetCore/issues/11789

Describe the bug

After passing value to RoleClaimType. It can no longer pass Authorize(Roles="").

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core ‘2.2’
  2. Run this code

Startup.cs

// Inject token service
services.AddScoped<ITokenService, TokenService>();

// Inject appsettings.json
var tokenConfigs = Configuration.GetSection("tokenManagement");
services.Configure<TokenConfigs>(tokenConfigs);

// JWT authorization setup
var token = tokenConfigs.Get<TokenConfigs>();
services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateLifetime = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(token.Secret)),
        ValidIssuer = token.Issuer,
        ValidAudience = token.Audience,
        NameClaimType = JwtClaimTypes.Name,
        RoleClaimType = JwtClaimTypes.Roles, // <---- Overriding, new value "role"
    };
});

TokenService.cs

public class TokenService : ITokenService
{
    private TokenConfigs _tokenManagement;

    public TokenService(IOptions<TokenConfigs> tokenManagement)
    {
        _tokenManagement = tokenManagement.Value;
    }

    public string GetAuthToken(TokenRequest req)
    {
        var claims = new[]
        {
            new Claim(JwtClaimTypes.Name, req.UserName),
            new Claim(JwtClaimTypes.Role, "xxxx"), // <----- Custom type
        };
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenManagement.Secret));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var jwtToken = new JwtSecurityToken(
            claims: claims,
            issuer: _tokenManagement.Issuer,
            audience: _tokenManagement.Audience,
            expires: DateTime.Now.Date.AddDays(1),
            signingCredentials: credentials
        );
        var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);

        return token;
    }
}

AuthController.cs

[AllowAnonymous]
[HttpPost]
public async Task<ActionResult<string>> Post([FromBody] TokenRequest req)
{
    string token = null;
    if (isValidUser)
    {
        token = _tokenService.GetAuthToken(req);
    }

    return Ok(token); // <--- Correct result returned
}

[HttpGet]
[Authorize(Roles = "admin,xxxx")] <------ Fail
public ActionResult<string> Get()
{
    var name = User.Identity.Name;
    var isAdmin = User.IsInRole("admin");
    var isXXXX = User.IsInRole("xxxx"); <---- False
    return Ok($"{name} {isAdmin} {isXXXX}");
}
  1. With these arguments ‘…’ N/A

  2. See error Fail to authorize. User.Claims contains incorrect claim type.

{http://schemas.microsoft.com/ws/2008/06/identity/claims/role: xxxx}

Expected behavior

Able to pass authorization. Claims should respect what’s passed in.

Screenshots

N/A

Additional context

Add any other context about the problem here. Include the output of dotnet --info

.NET Core SDK (reflecting any global.json):
 Version:   2.2.204
 Commit:    8757db13ec

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.2.204\

Host (useful for support):
  Version: 2.2.5
  Commit:  0a3c9209c0

.NET Core SDKs installed:
  2.1.201 [C:\Program Files\dotnet\sdk]
  2.1.202 [C:\Program Files\dotnet\sdk]
  2.1.400 [C:\Program Files\dotnet\sdk]
  2.1.401 [C:\Program Files\dotnet\sdk]
  2.1.402 [C:\Program Files\dotnet\sdk]
  2.1.403 [C:\Program Files\dotnet\sdk]
  2.1.500 [C:\Program Files\dotnet\sdk]
  2.1.502 [C:\Program Files\dotnet\sdk]
  2.1.503 [C:\Program Files\dotnet\sdk]
  2.1.504 [C:\Program Files\dotnet\sdk]
  2.1.505 [C:\Program Files\dotnet\sdk]
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.1.604 [C:\Program Files\dotnet\sdk]
  2.1.700 [C:\Program Files\dotnet\sdk]
  2.2.204 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.3-servicing-26724-03 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
clauzfcommented, Jul 5, 2019

Hi @clauzf - the issue here is the JwtSecurityTokenHandler performs inbound ‘type’ mapping.

You can disable the mapping on the application level: JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

or per instance (assuming that you are using default SecurityTokenValidator(s)):

...

.AddJwtBearer(x =>
{
    if (x.SecurityTokenValidators.FirstOrDefault() is JwtSecurityTokenHandler jwtSecurityTokenHandler)
        jwtSecurityTokenHandler.MapInboundClaims = false;
});

Thanks @GeoK , it works. Though I still found there is a bit inconsistency, since without your codes, the “name” claim type won’t be mapped into internal claim type, only “role” claim type gets mapped.

Also I’m curious about the syntax you used, x.SecurityTokenValidators.FirstOrDefault() is JwtSecurityTokenHandler did a boolean check, then you can still assign the FirstOrDefault result to jwtSecurityTokenHandler? It works, but feels like magic, is this some new C# syntax sugar?

1reaction
GeoKcommented, Jul 3, 2019

Hi @clauzf - the issue here is the JwtSecurityTokenHandler performs inbound ‘type’ mapping.

You can disable the mapping on the application level: JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

or per instance (assuming that you are using default SecurityTokenValidator(s)):

...

.AddJwtBearer(x =>
{
    if (x.SecurityTokenValidators.FirstOrDefault() is JwtSecurityTokenHandler jwtSecurityTokenHandler)
        jwtSecurityTokenHandler.MapInboundClaims = false;
});
Read more comments on GitHub >

github_iconTop Results From Across the Web

Value set for TokenValidationParameters.RoleClaimType ...
Value set for TokenValidationParameters.RoleClaimType converts string to PascalCase causing JWT to be invalidated.
Read more >
Problems when adding Azure AD Authentication and ...
When using Admin->Access Rights->Set Access Rights can I not see the roles ... TokenValidationParameters = new TokenValidationParameters
Read more >
Acces Token With .Net Core Api
Hello to all. I'm working with .net core 2.0 web api and web application. My web application has been configured to login/logut using...
Read more >
User Roles - Microsoft Q&A
When I try to access the role using the following code [user.IsInRole("Admin")] , it does not seem to recognize the "Admin" role. I...
Read more >
Architecting-Cloud-Native-NET-Apps-for-Azure.pdf
This guide begins by defining cloud native and introducing a reference application built using cloud- native principles and technologies.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found