Reputation: 39474
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'?
Upvotes: 48
Views: 35244
Reputation: 91
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); });
Upvotes: 1
Reputation: 1467
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.
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
Upvotes: 39
Reputation: 401
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.
Upvotes: 30
Reputation: 3646
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.)
Upvotes: 9
Reputation: 27578
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);
Upvotes: 15