azure-activedirectory-identitymodel-extensions-for-dotnet
azure-activedirectory-identitymodel-extensions-for-dotnet copied to clipboard
Options to retrieve the entire JWT payload (or header) as a JsonElement
In OpenIddict, there are cases where I need to get the entire JWT payload as a JsonElement
(always representing a JSON object by definition).
To achieve that, the current bits use the stringified EncodedPayload
property:
using var document = JsonDocument.Parse(Base64UrlEncoder.Decode(token.EncodedPayload));
// ...
It works, but it's kinda inefficient as it requires re-parsing the payload, which is something JsonWebToken
already does internally. With the move to System.Text.Json
, is there now a better way to achieve that?
Thanks.
/cc @brentschmaltz @jennyf19
@kevinchalet can you describe your scenario? When will you want the JsonElement.
OpenIddict's server offers an opt-in authorization request caching feature that stores the actual payload as an encrypted JWT in a distributed cache and redirects the user agent to the authorization endpoint with just a request_id
parameter attached, which allows working around large URI issues in delegated authentication scenarios. It basically works like request_uri
, but the payload is stored by the server itself and not by the client.
For that, we need to preserve the exact type of each parameters (i.e we can't store/restore everything as string
claims), so JsonElement
is directly used.
Here's the code doing that:
using var document = JsonDocument.Parse(
Base64UrlEncoder.Decode(((JsonWebToken) result.SecurityToken).InnerToken.EncodedPayload));
if (document.RootElement.ValueKind is not JsonValueKind.Object)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0117));
}
// Restore the request parameters from the serialized payload.
foreach (var parameter in document.RootElement.EnumerateObject())
{
if (!context.Request.HasParameter(parameter.Name))
{
context.Request.AddParameter(parameter.Name, parameter.Value.Clone());
}
}
If the raw JsonElement
was exposed, it would avoid having to re-parse the JSON payload:
var element = ((JsonWebToken) result.SecurityToken).InnerToken.JsonElement;
if (element.ValueKind is not JsonValueKind.Object)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0117));
}
// Restore the request parameters from the serialized payload.
foreach (var parameter in element.EnumerateObject())
{
if (!context.Request.HasParameter(parameter.Name))
{
context.Request.AddParameter(parameter.Name, parameter.Value.Clone());
}
}
Hope it's clear 😄
@kevinchalet You want to be able to obtain each property in the token as a JsonElement, to put them somewhere. Do you still the token deserialized? We may need to provide you with a hook where you can examine each property as we look through here: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/9a5e3ae3c7136d815241ff4a82f345907d6240d4/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs#L31
Another option would be for a user to provide a location to write the JsonElements.
@kevinchalet You want to be able to obtain each property in the token as a JsonElement, to put them somewhere. Do you still the token deserialized?
Actually, for this scenario, I don't need the claims to be materialized as strongly typed CLR objects, I just need a JsonElement
pointer that is used by OpenIddictParameter
as a delayed accessor (of course, it's likely Wilson itself would need to materialize things like iss
or iat
as you said in the other thread) 😃
Note: if it's too complicated or isn't a good fit for the new serialization model, it's not a huge deal, re-parsing the JWT payload is not the most efficient thing, but it works, so... 😄
@kevinchalet we've decided not to take it right now, due to our tight deadlines. Probably post-GA.
@kevinchalet if you had a callback that had the utf8bytes, would you be able to use that?
@kevinchalet we are going to be adding extensibility when reading the JsonWebToken, this feature might fix in. Any thoughts?
@kevinchalet if you had a callback that had the utf8bytes, would you be able to use that?
Interesting, what would it look like concretely? If you have something ready to test, I'd love to give it a try 😃
I'm thinking here: having a callback that passes the ReadOnlySpan