Answered by:
ClaimTypes value is different than what it seems

Question
-
User1501362304 posted
Hi,
I am generating a jwt token and giving some claims like
var tokenDescriptor = new SecurityTokenDescriptor { Issuer = "Issuer", Audience = "Audience", Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Sid, "1"), new Claim(ClaimTypes.Name, "test user"), new Claim(ClaimTypes.Email, "test@gmail.com"), new Claim(ClaimTypes.Role, "User") }), Expires = DateTime.UtcNow.AddMinutes(15), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("secret key")), SecurityAlgorithms.HmacSha256Signature) }; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor);
But when I inspect the token on jwt.io site then claim property names are
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid": "1", "unique_name": "test user", "email": "test@gmail.com", "role": "User", "nbf": 1602438310, "exp": 1602438430, "iat": 1602438310, "iss": "issuer", "aud": "audience" }
While getting claim from token using same ClaimTypes.Email then it throws null reference error. Also while value for ClaimTypes.Email is http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress then why it is showing simply "email" in jwt.io while the name for "Sid" claim seems correctly?
Thanks
Sunday, October 11, 2020 6:36 PM
Answers
-
User-1330468790 posted
Hi vkagrawal,
The reason is pretty simple as it is a by-design behavior.
There is a outbound transformation when handler is creating a token. The handler will traverse a property called "longToShortClaimTypeMapping" to search if there is any mapping for the long names (like http://xxxx/name). If finds a map, then the outbound name will be changed to a short one.
You could refer to the source code from below links:
I extract related codes to make the explanation more clear.
JwtSecurityTokenHandler Instantiation:
public JwtSecurityTokenHandler() { if (_mapInboundClaims) _inboundClaimTypeMap = new Dictionary<string, string>(DefaultInboundClaimTypeMap); else _inboundClaimTypeMap = new Dictionary<string, string>(); _outboundClaimTypeMap = new Dictionary<string, string>(DefaultOutboundClaimTypeMap); _inboundClaimFilter = new HashSet<string>(DefaultInboundClaimFilter); _outboundAlgorithmMap = new Dictionary<string, string>(DefaultOutboundAlgorithmMap); }
DefaultOutboundClaimTypeMap:
public static IDictionary<string, string> DefaultOutboundClaimTypeMap = ClaimTypeMapping.OutboundClaimTypeMap;
ClaimTypeMapping.OutboundClaimTypeMap:
public static IDictionary<string, string> OutboundClaimTypeMap { get { return longToShortClaimTypeMapping; } }
longToShortClaimTypeMapping:
shortToLongClaimTypeMapping = new Dictionary<string, string> { { JwtRegisteredClaimNames.Actort, ClaimTypes.Actor }, { JwtRegisteredClaimNames.Birthdate, ClaimTypes.DateOfBirth }, { JwtRegisteredClaimNames.Email, ClaimTypes.Email }, { JwtRegisteredClaimNames.FamilyName, ClaimTypes.Surname }, { JwtRegisteredClaimNames.Gender, ClaimTypes.Gender }, { JwtRegisteredClaimNames.GivenName, ClaimTypes.GivenName }, { JwtRegisteredClaimNames.NameId, ClaimTypes.NameIdentifier }, { JwtRegisteredClaimNames.Sub, ClaimTypes.NameIdentifier }, { JwtRegisteredClaimNames.Website, ClaimTypes.Webpage }, { JwtRegisteredClaimNames.UniqueName, ClaimTypes.Name }, { "oid", "http://schemas.microsoft.com/identity/claims/objectidentifier" }, { "scp", "http://schemas.microsoft.com/identity/claims/scope" }, { "tid", "http://schemas.microsoft.com/identity/claims/tenantid" }, { "acr", "http://schemas.microsoft.com/claims/authnclassreference" }, { "adfs1email", "http://schemas.xmlsoap.org/claims/EmailAddress" }, { "adfs1upn", "http://schemas.xmlsoap.org/claims/UPN" }, { "amr", "http://schemas.microsoft.com/claims/authnmethodsreferences" }, { "authmethod", ClaimTypes.AuthenticationMethod }, { "certapppolicy", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/applicationpolicy" }, { "certauthoritykeyidentifier", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/authoritykeyidentifier" }, { "certbasicconstraints", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/basicconstraints" }, { "certeku", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/eku" }, { "certissuer", "http://schemas.microsoft.com/2012/12/certificatecontext/field/issuer" }, { "certissuername", "http://schemas.microsoft.com/2012/12/certificatecontext/field/issuername" }, { "certkeyusage", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/keyusage" }, { "certnotafter", "http://schemas.microsoft.com/2012/12/certificatecontext/field/notafter" }, { "certnotbefore", "http://schemas.microsoft.com/2012/12/certificatecontext/field/notbefore" }, { "certpolicy", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatepolicy" }, { "certpublickey", ClaimTypes.Rsa }, { "certrawdata", "http://schemas.microsoft.com/2012/12/certificatecontext/field/rawdata" }, { "certserialnumber", ClaimTypes.SerialNumber }, { "certsignaturealgorithm", "http://schemas.microsoft.com/2012/12/certificatecontext/field/signaturealgorithm" }, { "certsubject", "http://schemas.microsoft.com/2012/12/certificatecontext/field/subject" }, { "certsubjectaltname", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/san" }, { "certsubjectkeyidentifier", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/subjectkeyidentifier" }, { "certsubjectname", "http://schemas.microsoft.com/2012/12/certificatecontext/field/subjectname" }, { "certtemplateinformation", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatetemplateinformation" }, { "certtemplatename", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatetemplatename" }, { "certthumbprint", ClaimTypes.Thumbprint }, { "certx509version", "http://schemas.microsoft.com/2012/12/certificatecontext/field/x509version" }, { "clientapplication", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-application" }, { "clientip", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-ip" }, { "clientuseragent", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent" }, { "commonname", "http://schemas.xmlsoap.org/claims/CommonName" }, { "denyonlyprimarygroupsid", ClaimTypes.DenyOnlyPrimaryGroupSid }, { "denyonlyprimarysid", ClaimTypes.DenyOnlyPrimarySid }, { "denyonlysid", ClaimTypes.DenyOnlySid }, { "devicedispname", "http://schemas.microsoft.com/2012/01/devicecontext/claims/displayname" }, { "deviceid", "http://schemas.microsoft.com/2012/01/devicecontext/claims/identifier" }, { "deviceismanaged", "http://schemas.microsoft.com/2012/01/devicecontext/claims/ismanaged" }, { "deviceostype", "http://schemas.microsoft.com/2012/01/devicecontext/claims/ostype" }, { "deviceosver", "http://schemas.microsoft.com/2012/01/devicecontext/claims/osversion" }, { "deviceowner", "http://schemas.microsoft.com/2012/01/devicecontext/claims/userowner" }, { "deviceregid", "http://schemas.microsoft.com/2012/01/devicecontext/claims/registrationid" }, { "endpointpath", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path" }, { "forwardedclientip", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-forwarded-client-ip" }, { "group", "http://schemas.xmlsoap.org/claims/Group" }, { "groupsid", ClaimTypes.GroupSid }, { "idp", "http://schemas.microsoft.com/identity/claims/identityprovider" }, { "insidecorporatenetwork", "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork" }, { "isregistereduser", "http://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser" }, { "ppid", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" }, { "primarygroupsid", ClaimTypes.PrimaryGroupSid }, { "primarysid", ClaimTypes.PrimarySid }, { "proxy", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-proxy" }, { "pwdchgurl", "http://schemas.microsoft.com/ws/2012/01/passwordchangeurl" }, { "pwdexpdays", "http://schemas.microsoft.com/ws/2012/01/passwordexpirationdays" }, { "pwdexptime", "http://schemas.microsoft.com/ws/2012/01/passwordexpirationtime" }, { "relyingpartytrustid", "http://schemas.microsoft.com/2012/01/requestcontext/claims/relyingpartytrustid" }, { "role", ClaimTypes.Role }, { "roles", ClaimTypes.Role }, { "upn", ClaimTypes.Upn }, { "winaccountname", ClaimTypes.WindowsAccountName }, }; longToShortClaimTypeMapping = new Dictionary<string, string>(); foreach (KeyValuePair<string, string> kv in shortToLongClaimTypeMapping) { if (longToShortClaimTypeMapping.ContainsKey(kv.Value)) { continue; } longToShortClaimTypeMapping.Add(kv.Value, kv.Key); } inboundClaimFilter = new HashSet<string>(); }
You could see that the "sid" claim is not included in above long-to-short mapping list so that you could not get a short name for sid.
Solution:
Manually add a mapping as below:
var tokenDescriptor = new SecurityTokenDescriptor { Issuer = "Issuer", Audience = "Audience", Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Sid, "1"), new Claim(ClaimTypes.Name, "test user"), new Claim(ClaimTypes.Email, "test@gmail.com"), new Claim(ClaimTypes.Role, "User") }), Expires = DateTime.UtcNow.AddMinutes(15), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("this is my custom Secret key for authnetication")), SecurityAlgorithms.HmacSha256Signature) }; var tokenHandler = new JwtSecurityTokenHandler(); var outBoundClaimTypeMap = tokenHandler.OutboundClaimTypeMap; // outBoundClaimTypeMap.Clear() will display long names for all claims. outBoundClaimTypeMap.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid", "sid"); var token = tokenHandler.CreateToken(tokenDescriptor); Console.WriteLine(token);
Result:
{"alg":"HS256","typ":"JWT"} .{"sid":"1", "unique_name":"test user", "email":"test@gmail.com", "role":"User", "nbf":1602470317, "exp":1602471214, "iat":1602470317, "iss":"Issuer", "aud":"Audience"}
Hope helps.
Best regards,
Sean
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Monday, October 12, 2020 2:39 AM
All replies
-
User-1330468790 posted
Hi vkagrawal,
The reason is pretty simple as it is a by-design behavior.
There is a outbound transformation when handler is creating a token. The handler will traverse a property called "longToShortClaimTypeMapping" to search if there is any mapping for the long names (like http://xxxx/name). If finds a map, then the outbound name will be changed to a short one.
You could refer to the source code from below links:
I extract related codes to make the explanation more clear.
JwtSecurityTokenHandler Instantiation:
public JwtSecurityTokenHandler() { if (_mapInboundClaims) _inboundClaimTypeMap = new Dictionary<string, string>(DefaultInboundClaimTypeMap); else _inboundClaimTypeMap = new Dictionary<string, string>(); _outboundClaimTypeMap = new Dictionary<string, string>(DefaultOutboundClaimTypeMap); _inboundClaimFilter = new HashSet<string>(DefaultInboundClaimFilter); _outboundAlgorithmMap = new Dictionary<string, string>(DefaultOutboundAlgorithmMap); }
DefaultOutboundClaimTypeMap:
public static IDictionary<string, string> DefaultOutboundClaimTypeMap = ClaimTypeMapping.OutboundClaimTypeMap;
ClaimTypeMapping.OutboundClaimTypeMap:
public static IDictionary<string, string> OutboundClaimTypeMap { get { return longToShortClaimTypeMapping; } }
longToShortClaimTypeMapping:
shortToLongClaimTypeMapping = new Dictionary<string, string> { { JwtRegisteredClaimNames.Actort, ClaimTypes.Actor }, { JwtRegisteredClaimNames.Birthdate, ClaimTypes.DateOfBirth }, { JwtRegisteredClaimNames.Email, ClaimTypes.Email }, { JwtRegisteredClaimNames.FamilyName, ClaimTypes.Surname }, { JwtRegisteredClaimNames.Gender, ClaimTypes.Gender }, { JwtRegisteredClaimNames.GivenName, ClaimTypes.GivenName }, { JwtRegisteredClaimNames.NameId, ClaimTypes.NameIdentifier }, { JwtRegisteredClaimNames.Sub, ClaimTypes.NameIdentifier }, { JwtRegisteredClaimNames.Website, ClaimTypes.Webpage }, { JwtRegisteredClaimNames.UniqueName, ClaimTypes.Name }, { "oid", "http://schemas.microsoft.com/identity/claims/objectidentifier" }, { "scp", "http://schemas.microsoft.com/identity/claims/scope" }, { "tid", "http://schemas.microsoft.com/identity/claims/tenantid" }, { "acr", "http://schemas.microsoft.com/claims/authnclassreference" }, { "adfs1email", "http://schemas.xmlsoap.org/claims/EmailAddress" }, { "adfs1upn", "http://schemas.xmlsoap.org/claims/UPN" }, { "amr", "http://schemas.microsoft.com/claims/authnmethodsreferences" }, { "authmethod", ClaimTypes.AuthenticationMethod }, { "certapppolicy", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/applicationpolicy" }, { "certauthoritykeyidentifier", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/authoritykeyidentifier" }, { "certbasicconstraints", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/basicconstraints" }, { "certeku", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/eku" }, { "certissuer", "http://schemas.microsoft.com/2012/12/certificatecontext/field/issuer" }, { "certissuername", "http://schemas.microsoft.com/2012/12/certificatecontext/field/issuername" }, { "certkeyusage", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/keyusage" }, { "certnotafter", "http://schemas.microsoft.com/2012/12/certificatecontext/field/notafter" }, { "certnotbefore", "http://schemas.microsoft.com/2012/12/certificatecontext/field/notbefore" }, { "certpolicy", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatepolicy" }, { "certpublickey", ClaimTypes.Rsa }, { "certrawdata", "http://schemas.microsoft.com/2012/12/certificatecontext/field/rawdata" }, { "certserialnumber", ClaimTypes.SerialNumber }, { "certsignaturealgorithm", "http://schemas.microsoft.com/2012/12/certificatecontext/field/signaturealgorithm" }, { "certsubject", "http://schemas.microsoft.com/2012/12/certificatecontext/field/subject" }, { "certsubjectaltname", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/san" }, { "certsubjectkeyidentifier", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/subjectkeyidentifier" }, { "certsubjectname", "http://schemas.microsoft.com/2012/12/certificatecontext/field/subjectname" }, { "certtemplateinformation", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatetemplateinformation" }, { "certtemplatename", "http://schemas.microsoft.com/2012/12/certificatecontext/extension/certificatetemplatename" }, { "certthumbprint", ClaimTypes.Thumbprint }, { "certx509version", "http://schemas.microsoft.com/2012/12/certificatecontext/field/x509version" }, { "clientapplication", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-application" }, { "clientip", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-ip" }, { "clientuseragent", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-client-user-agent" }, { "commonname", "http://schemas.xmlsoap.org/claims/CommonName" }, { "denyonlyprimarygroupsid", ClaimTypes.DenyOnlyPrimaryGroupSid }, { "denyonlyprimarysid", ClaimTypes.DenyOnlyPrimarySid }, { "denyonlysid", ClaimTypes.DenyOnlySid }, { "devicedispname", "http://schemas.microsoft.com/2012/01/devicecontext/claims/displayname" }, { "deviceid", "http://schemas.microsoft.com/2012/01/devicecontext/claims/identifier" }, { "deviceismanaged", "http://schemas.microsoft.com/2012/01/devicecontext/claims/ismanaged" }, { "deviceostype", "http://schemas.microsoft.com/2012/01/devicecontext/claims/ostype" }, { "deviceosver", "http://schemas.microsoft.com/2012/01/devicecontext/claims/osversion" }, { "deviceowner", "http://schemas.microsoft.com/2012/01/devicecontext/claims/userowner" }, { "deviceregid", "http://schemas.microsoft.com/2012/01/devicecontext/claims/registrationid" }, { "endpointpath", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path" }, { "forwardedclientip", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-forwarded-client-ip" }, { "group", "http://schemas.xmlsoap.org/claims/Group" }, { "groupsid", ClaimTypes.GroupSid }, { "idp", "http://schemas.microsoft.com/identity/claims/identityprovider" }, { "insidecorporatenetwork", "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork" }, { "isregistereduser", "http://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser" }, { "ppid", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" }, { "primarygroupsid", ClaimTypes.PrimaryGroupSid }, { "primarysid", ClaimTypes.PrimarySid }, { "proxy", "http://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-proxy" }, { "pwdchgurl", "http://schemas.microsoft.com/ws/2012/01/passwordchangeurl" }, { "pwdexpdays", "http://schemas.microsoft.com/ws/2012/01/passwordexpirationdays" }, { "pwdexptime", "http://schemas.microsoft.com/ws/2012/01/passwordexpirationtime" }, { "relyingpartytrustid", "http://schemas.microsoft.com/2012/01/requestcontext/claims/relyingpartytrustid" }, { "role", ClaimTypes.Role }, { "roles", ClaimTypes.Role }, { "upn", ClaimTypes.Upn }, { "winaccountname", ClaimTypes.WindowsAccountName }, }; longToShortClaimTypeMapping = new Dictionary<string, string>(); foreach (KeyValuePair<string, string> kv in shortToLongClaimTypeMapping) { if (longToShortClaimTypeMapping.ContainsKey(kv.Value)) { continue; } longToShortClaimTypeMapping.Add(kv.Value, kv.Key); } inboundClaimFilter = new HashSet<string>(); }
You could see that the "sid" claim is not included in above long-to-short mapping list so that you could not get a short name for sid.
Solution:
Manually add a mapping as below:
var tokenDescriptor = new SecurityTokenDescriptor { Issuer = "Issuer", Audience = "Audience", Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Sid, "1"), new Claim(ClaimTypes.Name, "test user"), new Claim(ClaimTypes.Email, "test@gmail.com"), new Claim(ClaimTypes.Role, "User") }), Expires = DateTime.UtcNow.AddMinutes(15), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("this is my custom Secret key for authnetication")), SecurityAlgorithms.HmacSha256Signature) }; var tokenHandler = new JwtSecurityTokenHandler(); var outBoundClaimTypeMap = tokenHandler.OutboundClaimTypeMap; // outBoundClaimTypeMap.Clear() will display long names for all claims. outBoundClaimTypeMap.Add("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid", "sid"); var token = tokenHandler.CreateToken(tokenDescriptor); Console.WriteLine(token);
Result:
{"alg":"HS256","typ":"JWT"} .{"sid":"1", "unique_name":"test user", "email":"test@gmail.com", "role":"User", "nbf":1602470317, "exp":1602471214, "iat":1602470317, "iss":"Issuer", "aud":"Audience"}
Hope helps.
Best regards,
Sean
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Monday, October 12, 2020 2:39 AM -
User1501362304 posted
Hi Sean Fang,
Thanks for the detailed explanation and I got the thing now.
Do you recommend that instead of using ClaimTypes.<claim_name> we should use our own enum so that it is always same at time of setting and getting claims from token without tweaking with default claim values or there is significance of provided ClaimTypes when dealing with User Identity in MVC UI part?
Thanks
Monday, October 12, 2020 8:18 AM -
User-1330468790 posted
Hi vkagrawal,
It depends on whether your project uses Identity or not.
@Tseng explains this very clear so that I directly extract the answer for you. (from https://stackoverflow.com/a/36196254/12871232)
Asp.Net Identity by default expects user name to be as
ClaimTypes.Name
(either users display name or mail, whatever you use), role asClaimTypes.Role
and user id (not necessary the row ID, just unique to identify the user i.e. a Guid or email address) asClaimTypes.NameIdentifier
. The defaults can also be seen here on GitHub.That being said, if you use custom claim types you need to tell it so in the
ClaimsIdentityOptions
when you configure Asp.Net Identity.The claim type set in
UserNameClaimType
is the one used, when you doUser.Identity.Name
to access it in your controller. If your claim type do not match the one inClaimsIdentityOptions
it will just simply return null.Best regards,
Sean
Tuesday, October 13, 2020 2:28 AM -
User1501362304 posted
Thanks Seam, will check that part and thanks for detailed explanation on claim values.
Thursday, October 15, 2020 3:50 PM