Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options to retrieve the entire JWT payload (or header) as a JsonElement #2260

Open
kevinchalet opened this issue Aug 25, 2023 · 10 comments
Open
Labels
Enhancement The issue is a new feature

Comments

@kevinchalet
Copy link
Contributor

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

@brentschmaltz
Copy link
Member

@kevinchalet can you describe your scenario?
When will you want the JsonElement.

@kevinchalet
Copy link
Contributor Author

kevinchalet commented Aug 28, 2023

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 😄

@brentschmaltz
Copy link
Member

brentschmaltz commented Aug 29, 2023

@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:

Another option would be for a user to provide a location to write the JsonElements.

@kevinchalet
Copy link
Contributor Author

@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) 😃

@kevinchalet
Copy link
Contributor Author

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... 😄

@jennyf19
Copy link
Collaborator

@kevinchalet we've decided not to take it right now, due to our tight deadlines. Probably post-GA.

@jennyf19 jennyf19 removed this from the 7.0.0-preview5 milestone Aug 29, 2023
@brentschmaltz
Copy link
Member

@kevinchalet if you had a callback that had the utf8bytes, would you be able to use that?

@brentschmaltz brentschmaltz added this to the December refresh milestone Oct 10, 2023
@brentschmaltz
Copy link
Member

@kevinchalet we are going to be adding extensibility when reading the JsonWebToken, this feature might fix in.
Any thoughts?

@brentschmaltz brentschmaltz added Enhancement The issue is a new feature and removed IdentityModel7x labels Feb 21, 2024
@kevinchalet
Copy link
Contributor Author

@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 😃

@brentschmaltz
Copy link
Member

I'm thinking here: having a callback that passes the ReadOnlySpan.
This will be the UTF8 chars that can be passed to a JsonDocument or Utf8JsonReader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement The issue is a new feature
Projects
None yet
Development

No branches or pull requests

3 participants