I am using the JwtSecurityTokenHandler for encoding and decoding/validating JWT.
I use the JWT as SET.
I have a high load of JWT, and I have some issues with the current implementation of the handler.
First of all, I have to pass by ClaimPrincipal and Claim which are perfect for the usage of JWT as identity, by useless for other usages. As Claims are basically a key/value string pair, I have to convert back to JSON if the value were a JSON content type. (JSON => string => JSON).
The second performance issue seems to be on the Base64UrlEncoder. Encoding produce a lot of string allocations, mainly due to the Split().
Do you plan to enhance the JWT performance, or even to split the packages (IdentityModel feature on one side, JWT management on the other side) ?
Am I using the correct library for my usage (a JWT not for the identity) ?
@ycrumeyrolle you would like ValidateToken that just returns JSON ?
Can you provide some details on the performance issue with Base64UrlEncoder (split)? How did you detect this?
A ValidateToken that just return the JSON may be a good idea. As a string? As a JObject?
Here is my current code for illustration:
if (!_jwtHandler.CanReadToken(token))
{
return JwtParsingResult.ParseError();
}
var set = _jwtHandler.ReadJwtToken(token);
var eventStream = await _eventReceiverMetadataProvider.GetAsync(set.Issuer);
if (eventStream == null)
{
return JwtParsingResult.IssuerError();
}
var validationParameters = new TokenValidationParameters
{
IssuerSigningKey = ConvertToSecurityKey(eventStream.FeedJwk),
ValidAudiences = eventStream.Audiences,
ValidIssuer = set.Issuer
};
SecurityToken securityToken;
_jwtHandler.ValidateToken(token, validationParameters, out securityToken);
var jwtSecurityToken = (JwtSecurityToken)securityToken;
var claims = new Dictionary<string, JToken>();
foreach (var claim in jwtSecurityToken.Claims)
{
switch (claim.ValueType)
{
case JsonClaimValueTypes.Json:
claims.Add(claim.Type, JObject.Parse(claim.Value));
break;
case JsonClaimValueTypes.JsonArray:
claims.Add(claim.Type, JArray.Parse(claim.Value));
break;
case ClaimValueTypes.String:
case ClaimValueTypes.Integer:
claims.Add(claim.Type, new JValue(claim.Value));
break;
default:
break;
}
}
// ...
About the Base64UrlEncoder, I just run a memory profiling and the Split() within the Encode() caused about 7.5% of the total allocation, which seems a lot just for removing trailing padding.
@ycrumeyrolle thinking about this some more, I am feeling stronger about introducing validation of a JWT and then expose the Json in the payload as JSON.net objects.
Another possibility is to have a JsonClaimsIdentity that has JSON, integrating into the asp.net stack will require to return Claims. The runtime doesn't support custom ClaimsIdentities, so it would be lost there. However, for your scenario, it may work.
Maybe something like
public virtual JObject/string ValidateToken(string token, TokenValidationParameters validationParameters) { }
?
@ycrumeyrolle I opened the following to add such a feature: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/660