azure-activedirectory-identitymodel-extensions-for-dotnet: Default 'sub' claim mapping does not resolve to ClaimsPrincipal.Identity.Name
To process a JWT, the API consumer is going to use most likely the JwtSecurityTokenHandler.ValidateToken
method, to get a ClaimsPrincipal
.
RFC7519 states for the registered ‘sub’ claim (emphasis mine):
The “sub” (subject) claim identifies the principal that is the subject of the JWT. The claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The “sub” value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.
The ClaimsPrincipal
object returned from the ValidateToken
method allows to identify the subject via its default interface with the Identity.Name
property only. There are no other default properties that expose a principal identity unless you’d query the claims.
Because the default mapping of the claims controlled with ClaimTypeMapping
links sub
to http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
, it does not match with the claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
used to query the Name
property.
Therefore, when a JWT has the sub
claim only, there is no easy API to access its value.
The default mapping should be changed to http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
, so that ClaimsPrincipal.Identity.Name
resolves to the sub
claim.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 32 (21 by maintainers)
Links to this issue
Commits related to this issue
- Fixed issue with incoming claim names being transformed by .NET — committed to Noah231515/MemUp by AndrewB21 3 years ago
- Update Startup.cs JWT unique user ID is in the subject - "sub" - claim. The claim also does not map correctly to the user without a known workaround of placing JwtSecurityTokenHandler.DefaultInbound... — committed to AiGhostMod/active-directory-aspnetcore-webapp-openidconnect-v2 by AiGhostMod 2 years ago
- Fix default JWT parse behaviour ("sub" rename) https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/415 — committed to irealworlds/licirom by trupples a year ago
- Fix default JWT parse behaviour ("sub" rename) https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/415 — committed to irealworlds/licirom by trupples a year ago
I personally think it is evil that the JWT handler converts the standard claim types to the Microsoft favoured ones.
you can turn that globally off by setting the static
DefaultInboundClaimTypeMap
property on the JWT handler.After that - yes do a
FindFirst
onsub
.For anyone hitting this with this issue - any ASP .NET 5 API you work on that is receiving JWTs, you need to ensure
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
gets called at the top of your ConfigureServices method in Startup otherwise standardized claim names will get transformed to legacy XML names.This is especially pertinent in a microservices architecture - this will need to be added to ALL API Gateways and ALL microservices to ensure that any tokens that are passed downstream don’t get auto-magically changed into the old legacy formats.
I just lost 2 hours to this - in 2020. Again, repeating what others have said above - it is not clearly documented and the issue has been open for a long time now. You want to reach new developers and not come across as ‘old’ Microsoft yet you favor supporting legacy code over just adding in common sense behaviour to a brand new framework. Its perfectly reasonable to clearly document a breaking change like that and expect people who are porting legacy projects to ensure this works well.
Making legacy projects work on a new framework is not how you ensure said new framework takes off - ensuring your APIs behave in ways that make sense and delight the developer experience is how you make .NET 5 fly. Please please please fix stuff like this. Im trying to pursuade my devs to move over to the new open source framework but this is like the third thing ive ran into this week that frustrates the developer experience.
@zvrba @khellang @bgever we created a new handler JsonWebTokenHandler that does not perform mapping. We need to give asp.net users a way to specify that handler. Additionally, it is about 25% faster.
We are working on it.
This continues to bite people in 2018… https://github.com/aspnet/Mvc/issues/7760
Yep, came here cause of same issue. Sub claim created via
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString())
after parsing/extracting from principal becomes
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
very confusing
Two workarounds for the current versions (tested with 5.0 RC2):
NameClaimType
on theTokenValidationParameters
:The first one is more “true” to the original JWT claims, as none of the claims will expand to “long” versions. Second one is the quickest operation, since only one claim type mapping needs to be overridden, and the
ClaimsPrincipal
will use default claim type to look up the name.Obviously, I still prefer to see the default behavior changed. 😄
@mafurman Could you elaborate “When we go async with the JsonWebTokenHandler” a bit more please?
sub is not the name of a user. It’s the unique user id.
@ggonzalez94 as soon as your Appdomain loads write:
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
What’s the framework’s recommended way of retrieving the sub (i.e. unique user id) from the ClaimsPrincipal object?
This would not work, because by default the claims are converted to their long values. That’s a framework feature, but not something I would expect when I’m just parsing a JWT.
So the following would work, but this behavior isn’t clear to someone calling the API:
The workarounds I’ve posted before work for me, or I could even query the claim as here, but I wonder how other consumers of the API could discover this implementation specific behavior?
@Joren-Thijs-KasparSolutions we recognize this is an issue. We added the static: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/cb465658fcd0cf315891c063d3111c7267b408af/src/System.IdentityModel.Tokens.Jwt/JwtSecurityTokenHandler.cs#L47 If set to false, then no mapping will take place.
Asp.net 8 will be using the JsonWebTokenHandler where mapping is false by default see: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/cb465658fcd0cf315891c063d3111c7267b408af/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs#L42
Just ran into this, what worked for me was:
There will be always some sort of mapping going on - but the JWT handler is the wrong place to do that IMO. That’s application logic.
So yes - I still opt for turning it off (now’s the last chance)