The type of System.Security.Claims.Claim.Value is to string which is contrary to the spec:
Claim Value
The value portion of a claim representation. A Claim Value can be
any JSON value.
This is big problem for us while creating a .NET Core app for a large established ecosystem where JWTs have object claims.
The problem is exacerbated by the fact that now System.IdentityModel.Tokens.Jwt library is dependent on this implementation in the way it deserialises JWTs into ClaimsIdentity and then into ClaimsPrincipal... (e.g. here or here)
Why is it contrary to the specification?
"any json value" includes json values, e.g. strings or numbers
If you take a look at the example in RFC 7523 and they use the following example:
{"iss":"https://jwt-idp.example.com",
"sub":"mailto:[email protected]",
"aud":"https://jwt-rp.example.net",
"nbf":1300815780,
"exp":1300819380,
"http://claims.example.com/member":true}
Do you write this full json object into one claim?
This should be decoded into multiple claims, e.g. issuer, subject, audience etc and as far as I see in your code references JwtSecurityToken just does that.
As for the data type, that's what ClaimValueType is for.
As far I understand the correct way of processing a raw JWT into a principal is by executing this:
JwtSecurityTokenHandler handler;
var principal = handler.ValidateToken(rawJwt, tokenValidationParameters, out validatedToken);
Here's an example JWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiIsImlzRW5jcnlwdGVkIjoiRmFsc2UiLCJ4NXQiOiI1M0VENjE1NTUwNTlBRDg3QUE4MkNBNTYwRTQ4QkIxMkM1MzdGOUY1IiwidmVyIjoiMi4xIn0.eyJhdWQiOiJIZWxpeC5TZWN1cml0eS5VdGlsIiwiaWF0IjoiMTQ3MjA5NjU1Ny43NDM3NiIsIm5iZiI6IjE0NzIwOTY1NDQuNzU3NTkiLCJDbGFpbVNldHMiOlt7IkNsYWltcyI6eyJzZXJ2ZXJJZCI6IkhlbGl4LkNvbnRhaW5lcnMuRGV2Iiwic2VydmVyVmVyc2lvbiI6ImRldiIsImlzc1g1dCI6IjUzRUQ2MTU1NTA1OUFEODdBQTgyQ0E1NjBFNDhCQjEyQzUzN0Y5RjUifSwiUHJvdmlkZXIiOiJIZWxpeC5Db250YWluZXJzLkRldi52ZGV2IiwiU2NoZW1hIjoiSGVsaXguQ29udGFpbmVyIiwiVmVyc2lvbiI6IlYxIn1dLCJpc3MiOiJIZWxpeC5Db250YWluZXJzLkRldi52ZGV2IiwiZXhwIjoiMTQ3MjA5Nzc0NC43NTg1OSIsInNzaWQiOiJjNmJkNzY3ZjE4YWU0ZTQyOTliMmY4YjJmNzhmODU1NSJ9.W8ARsO3IKMO_CBl5fMkgTEkPmoZZvjaX46-mmVHqT5hQAbQVBmnc18B9VxsSS34YKVE2dBQwZHjhu2ROSOCKeuHOqHjjS_HuSdDLOdi7rJUdpKw1GE-lBqxzUPojAlUvLRlq7KjbwipXd7bJyMk7chVU9r548pmljDAlm7SOqmM-qcZ8X0sgQcDxxZoacJiL9xQpbJPi9CVHC_ms2LJhm6AFcCNTlRZNgAmMvoIBWfjXVsVC92HFgqd_qTpMvudTs216LIfslpJC0WiU4SFWKV2Bt5rGGVVqSe4vXb4W1Si58t8ORcepRnkZ1jkEuKf2VpHTEw0ylwX_BLqnnKdavQ
Decoded to JSON:
{
"typ": "JWT",
"alg": "RS512",
"isEncrypted": "False",
"x5t": "53ED61555059AD87AA82CA560E48BB12C537F9F5",
"ver": "2.1"
}
{
"aud": "Helix.Security.Util",
"iat": "1472096557.74376",
"nbf": "1472096544.75759",
"ClaimSets": [
{
"Claims": {
"serverId": "Helix.Containers.Dev",
"serverVersion": "dev",
"issX5t": "53ED61555059AD87AA82CA560E48BB12C537F9F5"
},
"Provider": "Helix.Containers.Dev.vdev",
"Schema": "Helix.Container",
"Version": "V1"
}
],
"iss": "Helix.Containers.Dev.vdev",
"exp": "1472097744.75859",
"ssid": "c6bd767f18ae4e4299b2f8b2f78f8555"
}
Once raw JWT is loaded into an instance of JwtSecurityToken it has the following Payload:

However its Claim are as follows:

The ClaimSets claim is serialised back to string...
Now a JwtSecurityTokenHandler takes the deserialised JWT and loads it into an instance of ClaimsIdentity:
foreach (Claim jwtClaim in jwt.Claims)
{
// snip ...
Claim c = new Claim(claimType, jwtClaim.Value, jwtClaim.ValueType, issuer, issuer, identity);
// snip ...
identity.AddClaim(c);
}
At the end of all of this, the principal has all claims serialised back to string which is incorrect and extremely inefficient

Thanks for the explanation about the claimset.
This indeed looks wrong.
There was a similar issue in IdentityServer https://github.com/IdentityServer/IdentityServer3/issues/1423
@brentschmaltz are you still planning to work on this one? We are trying to close down the issues of API incompatibility this month.
I will investigate, we were focused on some 1.1.0 items.
From: Dan Moseley [email protected]
Sent: Tuesday, November 8, 2016 3:26 PM
To: dotnet/corefx
Cc: BrentSchmaltz; Mention
Subject: Re: [dotnet/corefx] System.Security.Claims.Claim value type incorrect (#11082)
@brentschmaltzhttps://github.com/brentschmaltz are you still planning to work on this one? We are trying to close down the issues of API incompatibility this month.
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com/dotnet/corefx/issues/11082#issuecomment-259292125, or mute the threadhttps://github.com/notifications/unsubscribe-auth/ADBoRUrLVaEgjIviTULWZD_w2WXJPRLGks5q8QUdgaJpZM4JrnVi.
@RussKie can you help me out with the version of System.IdentityModel.Tokens.Jwt you are using?
With 5.1.0 I am seeing the ClaimsPrincipal.Identity containing 7 claims. One of the claims is Type: ClaimsSets, Value: {"Claims":{"serverId":"Helix.Containers.Dev","serverVersion":"dev","issX5t":"53ED61555059AD87AA82CA560E48BB12C537F9F5"},"Provider":"Helix.Containers.Dev.vdev","Schema":"Helix.Container","Version":"V1"}, ValueType: JSON.
We did some work in 5.0 to set the ValueType and leave the Value as JSON.
I'm insanly busy for next few weeks, but I'll try to set aside some time to
test it
On 22/11/2016 5:52 PM, "BrentSchmaltz" [email protected] wrote:
@RussKie https://github.com/RussKie can you help me out with the
version of System.IdentityModel.Tokens.Jwt you are using?
With 5.1.0 I am seeing the ClaimsPrincipal.Identity containing 7 claims.
One of the claims is Type: ClaimsSets, Value: {"Claims":{"serverId":"Helix.
Containers.Dev","serverVersion":"dev","issX5t":"
53ED61555059AD87AA82CA560E48BB12C537F9F5"},"Provider":"
Helix.Containers.Dev.vdev","Schema":"Helix.Container","Version":"V1"},
ValueType: JSON.We did some work in 5.0 to set the ValueType and leave the Value as JSON.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/corefx/issues/11082#issuecomment-262162040,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEMyXgIWw3hq84LSMUjCEDvejZq-DRNTks5rApFDgaJpZM4JrnVi
.
@RussKie thanks, I will post some sample code. Wait for it.
@RussKie I wrote some samples to help us understand the issue: https://github.com/brentschmaltz/Wilson5x/blob/master/src/JsonClaims/JwtWithJsonSubClaims.cs
Thanks @brentschmaltz. It works and I can see new type _JSON_.
Though I can't really see how it addresses my issue, perhaps I'm missing something.
I've created a condensed sample of our workflow: https://github.com/RussKie/Playpen/blob/master/TestHarness.cs.
I see that my claim is of type JSON too, but I can't access to values within unless I parse JSON object, can I?

If this is the only way to access them then it is not really a solution since it would be a serious performance overhead....
Ideally after line 92 in my sample I should be able to write something like:
var myIdentity = principal.Identities[0] as MyIdentity;
var claimset = myIdentity.ClaimSets[0];
var schema = claimset.Schema;
var serverVersion = claimset.Claims[0]["serverVersion"]; // = "dev"
One ways of achieving this is to be able to interject into the claimsidentity creation process and create my own custom identity instead. Though at this stage I am not sure how I can achieve this.
Your thoughts are appreciated.
Thanks
At the ClaimsIdentity level, all values are strings.
The 'token' returned from validateToken is a JwtSecurityToken.
JwtSecurityToken.JwtPayload is a dictionary of objects. The complex JSON claims are Json.net JObject and JArray.
If you work with the JwtPayload, then you can work directly with the JArray.
From: RussKie notifications@github.com
Sent: Tuesday, November 29, 2016 8:20 PM
To: dotnet/corefx
Cc: BrentSchmaltz; Mention
Subject: Re: [dotnet/corefx] System.Security.Claims.Claim value type incorrect (#11082)
Thanks @brentschmaltzhttps://github.com/brentschmaltz. It works and I can see new type JSON.
Though I can't really see how it addresses my issue, perhaps I'm missing something.
I've created a condensed sample of our workflow: https://github.com/RussKie/Playpen/blob/master/TestHarness.cs.
I see that my claim is of type JSON too, but I can't access to values within unless I parse JSON object, can I?
[image]https://cloud.githubusercontent.com/assets/4403806/20739578/f9d14f86-b70f-11e6-8aeb-d950bee02406.png
If this is the only way to access them then it is not really a solution since it would be a serious performance overhead....
Ideally after line 92 in my sample I should be able to write something like:
var myIdentity = principal.Identities[0] as MyIdentity;
var claimset = myIdentity.ClaimSets[0];
var schema = claimset.Schema;
var serverVersion = claimset.Claims[0]["serverVersion"]; // = "dev"
One ways of achieving this is to be able to interject into the claimsidentity creation process and create my own custom identity instead. Though at this stage I am not sure how I can achieve this.
Your thoughts are appreciated.
Thanks
-
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com/dotnet/corefx/issues/11082#issuecomment-263779412, or mute the threadhttps://github.com/notifications/unsubscribe-auth/ADBoRbTyf3fNJWd3OS0OxTRJ5WNzNseTks5rDPmjgaJpZM4JrnVi.
At the ClaimsIdentity level, all values are strings.
I am the only one seeing the problem with this? 😱
As an end-consumer app developer I should not be locked in dealing with strings, it is just too impractical. I should be dealing with whatever objects fit my business requirements.
Ultimately in the code the developers don't deal with JWTs (it is a mere transport object), they deal the identities.
To put this in a context.
Previously HTTP context would expose the user as IPrincipal (https://github.com/Microsoft/referencesource/blob/master/System.Web/HttpContext.cs#L1237). Now in OWIN/.NETCore HttpContext it is no longer the case, it is ClaimsPrincipal. This means I can't substitute it with a custom principal. Because I can't put my custom principal I can't expose my custom identity as it is now of ClaimsIdentity type. And the ClaimsIdentity only provides me with strings....
For example:
public interface IMyIdentity : IIdentity
{
IdentityClaimSetCollection ClaimSets { get; set; }
}
public class MyIdentity : IMyIdentity
{
IdentityClaimSetCollection ClaimSets { get; set; }
}
public class MyPrincipal : IPrincipal
{
public MyIdentity Identity { get; set; }
IIdentity IPrincipal.Identity
{
get { return Identity; }
}
}
HttpContext.Current.User = new MyPrincipal();
I can no longer do anything like this in OwinContext / .NETCore HttpContext...
I don't agree with the current implementation of ClaimsIdentity - it is too rigid. Either it has to support more than just string claims or there has to be a mechanism for me to inject my custom principal/identity which support other than string claims.
So far I haven't found any ways to achieve this cleanly. 😢
As far as the original bug - I think what you offered can be considered as a fix - JWT now supports object claims as JSON...
As far as dealing with those claims once they are loaded into a ClaimsIdentity - it is still a major loss of functionality without a workaround.
I am happy to have this ticket closed and raise a new one for the ClaimsIdentity issue, if you wish.
Equally happy to continue on this ticket.
@RussKie @Tratcher it was considered by design that asp.net core would not support CustomClaimsPrincipal to flow through the runtime. However, it may be possible to have a CustomClaimsIdentity OR a CustomClaim to return the Json. There is enough extensibility in IdentityModel, but I am not sure if it will round trip through Cookies. However, for Bearer, cookies may not be important. I will squeeze in some time shortly to put together an POC and ping you.
Thanks. Yes, the problem could be resolved if ClaimsPrincipal was not restricted to ClaimsIndetity only.
I am afraid I am unsure how to interpret your last two sentences. We aren't using either OAuth or OIDC.
I think it is orthogonal how and where JWT comes from to how structured claims can be retrieved from the re-hydrated identity.
@RussKie The last two sentences were related to asp.net users. For users of IdentityModel, we have extensibility that allows you to create your own custom ClaimsIdentity returned from ValidateToken. Both JwtSecurityTokenHandler.CreateClaimsIdentity and TokenValidationParameters.CreateClaimsIdentity are virtual and between the two, you can have a custom ClaimsIdentity OR CustomClaims. We do not have a delegate for CreateClaimsIdentity, as we do for many other overloads. Deriving is necessary.
Thank you for the clarification. I'll have a look.
I'm not actually building any .NETCore apps at the moment and it's been awhile since I looked under the cover. Thanks for the pointer.
Both JwtSecurityTokenHandler.CreateClaimsIdentity and TokenValidationParameters.CreateClaimsIdentity are virtual and between the two, you can have a custom ClaimsIdentity OR CustomClaims.
I've been staring at these two for a bit now and I still feel kind of stuck.
ClaimsIdentity deals only with claims type of Claim, and that type defines claims as string:string.
Even if I create my own identity type derived from ClaimsIdentity I have no meaningful way to override this. Not deriving is not an option since ClaimsPrincipal deals with ClaimsIdentitys.
Is it conceivable to have something like the following at all?
public class ClaimsIdentity<TClaim> : IIdentity
where TClaim: Claim
{
public ClaimsIdentity(IEnumerable<TClaim> claims) { ... }
public virtual void AddClaim(TClaim claim) { ... }
public virtual void AddClaims(IEnumerable<TClaim> claims) { ... }
}
or extract an interface from Claim type and use it instead of the concrete class?
@brentschmaltz can you help resolve this one?
it was considered by design that asp.net core would not support CustomClaimsPrincipal to flow through the runtime.
@brentschmaltz @Tratcher are you able to share the reasoning behind this decision?
One of the primary reasons is that we need to serialize an identity into a cookie. We can't serialize and deserialize arbitrary types. There are also several other operations we do like merging multiple ClaimsPrincipals into a single principal.
@bartonjs @steveharter can you please unravel where this is at and whether it meets the 2.0 criteria (eg significant blocker to port from desktop). If not please move to Future.
@brentschmaltz @tratcher can you please move to Future or close this as appropriate?
The users are still significantly handicapped.
As per the spec claim values can be objects (this is now fixed, thanks) but JWT deserialisation process serialises everything back to string because the .NETCore implementation now forces the consumers into using ClaimsPrincipal and ClaimsIdentity without any meaningful workaround.
This makes it hard to build high performance solutions.
I am happy to have this ticket closed and raise a new one for the ClaimsIdentity issue, if you wish.
Equally happy to continue on this ticket.
If you don't mind, please open a new issue as you suggested since the original post/question has been resolved. That should make it far easier to move on and discuss.
I'm closing this one as the OP has been resolved.
Most helpful comment
As far I understand the correct way of processing a raw JWT into a principal is by executing this:
Here's an example JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiIsImlzRW5jcnlwdGVkIjoiRmFsc2UiLCJ4NXQiOiI1M0VENjE1NTUwNTlBRDg3QUE4MkNBNTYwRTQ4QkIxMkM1MzdGOUY1IiwidmVyIjoiMi4xIn0.eyJhdWQiOiJIZWxpeC5TZWN1cml0eS5VdGlsIiwiaWF0IjoiMTQ3MjA5NjU1Ny43NDM3NiIsIm5iZiI6IjE0NzIwOTY1NDQuNzU3NTkiLCJDbGFpbVNldHMiOlt7IkNsYWltcyI6eyJzZXJ2ZXJJZCI6IkhlbGl4LkNvbnRhaW5lcnMuRGV2Iiwic2VydmVyVmVyc2lvbiI6ImRldiIsImlzc1g1dCI6IjUzRUQ2MTU1NTA1OUFEODdBQTgyQ0E1NjBFNDhCQjEyQzUzN0Y5RjUifSwiUHJvdmlkZXIiOiJIZWxpeC5Db250YWluZXJzLkRldi52ZGV2IiwiU2NoZW1hIjoiSGVsaXguQ29udGFpbmVyIiwiVmVyc2lvbiI6IlYxIn1dLCJpc3MiOiJIZWxpeC5Db250YWluZXJzLkRldi52ZGV2IiwiZXhwIjoiMTQ3MjA5Nzc0NC43NTg1OSIsInNzaWQiOiJjNmJkNzY3ZjE4YWU0ZTQyOTliMmY4YjJmNzhmODU1NSJ9.W8ARsO3IKMO_CBl5fMkgTEkPmoZZvjaX46-mmVHqT5hQAbQVBmnc18B9VxsSS34YKVE2dBQwZHjhu2ROSOCKeuHOqHjjS_HuSdDLOdi7rJUdpKw1GE-lBqxzUPojAlUvLRlq7KjbwipXd7bJyMk7chVU9r548pmljDAlm7SOqmM-qcZ8X0sgQcDxxZoacJiL9xQpbJPi9CVHC_ms2LJhm6AFcCNTlRZNgAmMvoIBWfjXVsVC92HFgqd_qTpMvudTs216LIfslpJC0WiU4SFWKV2Bt5rGGVVqSe4vXb4W1Si58t8ORcepRnkZ1jkEuKf2VpHTEw0ylwX_BLqnnKdavQDecoded to JSON:
Once raw JWT is loaded into an instance of

JwtSecurityTokenit has the followingPayload:However its

Claimare as follows:The
ClaimSetsclaim is serialised back to string...Now a
JwtSecurityTokenHandlertakes the deserialised JWT and loads it into an instance ofClaimsIdentity:At the end of all of this, the principal has all claims serialised back to

stringwhich is incorrect and extremely inefficient