Why is ClaimTypes.NameIdentifier not mapping to 'sub'?
Asked Answered
A

5

44

Using ASP.NET Core 2.2 and Identity Server 4 I have the following controller:

[HttpGet("posts"), Authorize]
public async Task<IActionResult> GetPosts() {

  var authenticated = this.User.Identity.IsAuthenticated;

  var claims = this.User.Identities.FirstOrDefault().Claims;

  var id = this.User.FindFirstValue(ClaimTypes.NameIdentifier);

}

I get all the claims but id is null ...

I checked all values in claims and I have a 'sub' claim with value 1.

Why is ClaimTypes.NameIdentifier not mapping to 'sub'?

Arrowworm answered 18/9, 2019 at 17:45 Comment(3)
Try setting this: JwtSecurityTokenHandler.DefaultMapInboundClaims = false; This is mostly to fix the fact the sub claim gets translated into the nameidentifier claim.Dewie
It didn't solve it ... id is still null.Arrowworm
It's a good question that's why I upvoted it (from -1 to 0 now) but your example is irrelavant to your question. If you see 'sub' in your claims, then FindFirstValue of "sub".Angeli
B
39
  1. To not let Microsoft Identity to override claim names you have to use JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); just before the app.UseAuthentication() in the API startup.

  2. Use direct "sub" claim instead of ClaimTypes.NameIdentifier e.g. var id = this.User.FindFirstValue("sub");

For further reference please see detailed discussion on it: https://github.com/IdentityServer/IdentityServer4/issues/2968#issuecomment-510996164

Beta answered 19/5, 2020 at 21:11 Comment(2)
This should be marked as the correct answer.Pride
Make sure to check the answer from @Dominion suggesting the newer JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear()Escritoire
U
23

Nan Yu's answer no longer works as of .NET 8 Preview 7. I think the idiomatic way to do it since then is to set JwtBearerOptions.MapInboundClaims to false in the call to AuthenticationBuilder.AddJwtBearer.


            services
                .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(jwtBearerOptions =>
                {
                    jwtBearerOptions.MapInboundClaims = false;
                });

Alternatively, in spirit of the old method, just change JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); to JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear();

See discussion in this GitHub issue.

Undesirable answered 14/8, 2023 at 1:5 Comment(4)
Thanks! Was VERY bizarre after updating to .NET 8 that suddenly FindFirstValue for UserIdClaimType was returning a username and not user ID.Escritoire
JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear(); was the one, thanks! Very bizarre to suddenly map incoming claims, thereby breaking what is effectively a contract. Happened to me after removing .netstandard2.0 from a multi-targeted library, which now only targets .NET 8.Cnossus
Be aware that you might need to change the NameClaimType and RoleClaimType as well.Liquidity
Thanks this worked for me after upgrading to .NET 8Dissonancy
L
15

I assume in OIDC configuration you have clear the inbound claim type map on the Microsoft JWT token handler with :

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

You can then manually setting the claim type mapping for claim sub:

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
Loudspeaker answered 19/9, 2019 at 2:21 Comment(5)
I tried your suggestion and is still null ... No idea what is going on ...Arrowworm
what if you delete this line : JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();Loudspeaker
With calling Clear I get the error 'System.ArgumentException' in System.Private.CoreLib.dll: 'An item with the same key has already been added. Key: sub'Arrowworm
try delete this line : JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); , and also this one JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier); , delete both codesLoudspeaker
Thanks a lot...ur code add NameIdentifier claim type when DefaultInboundClaimTypeMap.Clear() fr some other issueTransudation
K
9

The static string ClaimTypes.NameIdentifier has the following value: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier

So no wonder it can't be used to look up the value of the sub claim.

If you know that the sub claim contains the user id, you can just do the lookup simply with the sub string.

ClaimTypes.NameIdentifier works for looking up the user id only in cases when the ClaimsPrincipal was created with the default inbound claim type mapping, which maps the sub claim to http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier.

But even in such cases it's more appropriate to use userManager.Options.ClaimsIdentity.UserIdClaimType, because this is the actual value used in the original mapping (which defaults to ClaimTypes.NameIdentifier, but can be customized).

(But yes, this whole mapping is quite confusing, and there are a number of Github issues opened where even MS devs lament that this is an evil existing solely for legacy reasons.)

Khalif answered 1/4, 2020 at 10:28 Comment(0)
E
0

Most likely the "sub" claim is called "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier". You can map it using (reference)

builder.Services.AddAuthentication(options =>
{
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
  .AddOpenIdConnect(options =>
  {
       // Other options...
       options.TokenValidationParameters = new TokenValidationParameters
       {
          NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
       };
  });

or if you are using Azure AD B2C

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
  builder.Configuration.Bind("AzureAdB2C", options);
  options.TokenValidationParameters.NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
},
options => { builder.Configuration.Bind("AzureAdB2C", options); });
Exercise answered 9/6 at 0:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.