Add JsonWebToken header-replacement constructor to avoid re-parsing payload#3530
Add JsonWebToken header-replacement constructor to avoid re-parsing payload#3530shankar-shubh wants to merge 4 commits into
Conversation
…ayload Add a public constructor, JsonWebToken(JsonWebToken jsonWebToken, string encodedHeader), that builds a token by replacing the header of an existing JWS while reusing its already-parsed payload and signature. The token is formed as 'encodedHeader.EncodedPayload.EncodedSignature'; only the replacement header is decoded and parsed, so the payload claim set is reused instead of being parsed a second time. This supports header-only transforms (for example PFT nonce hashing), where callers previously had to reassemble the token string and fully re-read it, incurring a redundant payload parse and allocation. Encrypted (JWE) source tokens are rejected with a new IDX14117 message because their plaintext payload is not available for reuse. Updates PublicAPI.Unshipped.txt and adds unit tests covering header replacement, payload/signature reuse, equivalence with a reparsed token, signature validation, null/empty guards, and the JWE rejection path. Adds JsonWebTokenHeaderReplacementBenchmarks isolating the new constructor against reassemble-and-reparse across token sizes (net8.0, MediumRun, MemoryDiagnoser): Minimal ~0.7 KB: 2549 ns / 2.63 KB -> 601 ns / 2.26 KB (-76% time, -14% alloc) Small ~1.9 KB: 16758 ns / 11.25 KB -> 818 ns / 4.81 KB (-95% time, -57% alloc) Typical ~4 KB: 47911 ns / 24.30 KB -> 1079 ns / 8.85 KB (-98% time, -64% alloc) The eliminated payload parse scales with payload size, so the win grows with the token: at a realistic ~4 KB token the constructor is ~44x faster and allocates 64% less.
|
@microsoft-github-policy-service agree company="Microsoft" |
|
Nice perf win. One thought on the public surface: the new ctor reuses the original signature with a swapped header. That's exactly right for the PFT nonce-hashing case the description mentions (the new header reconstructs the originally-signed bytes), but as a general public ctor it lets any caller swap a header and silently keep an invalid signature. Could we expose this through Same payload-reuse perf, but it sits next to |
|
@iNinja Have updated the PR as per the comment. |
| /// </remarks> | ||
| /// <exception cref="ArgumentNullException">Thrown if <paramref name="jsonWebToken"/> is null.</exception> | ||
| /// <exception cref="ArgumentNullException">Thrown if <paramref name="encodedHeader"/> is null or empty.</exception> | ||
| /// <exception cref="ArgumentException">Thrown if <paramref name="jsonWebToken"/> is an encrypted token (JWE), as its payload cannot be reused.</exception> |
There was a problem hiding this comment.
nit: it's not clear why JWEs aren't supported
Add JsonWebToken header-replacement constructor to avoid re-parsing payload
Summary of the changes (Less than 80 chars)
Add a JsonWebToken constructor that reuses a parsed payload on header replacement
Description
Adds a public constructor
JsonWebToken(JsonWebToken jsonWebToken, string encodedHeader)that builds a token by replacing the header of an existing JWS while reusing its already-parsed payload and signature. The token is formed asencodedHeader.EncodedPayload.EncodedSignature; only the replacement header is decoded and parsed, so the payload claim set is reused instead of being parsed a second time.This supports header-only transforms (for example PFT nonce hashing) where callers previously had to reassemble the token string and fully re-read it via
ReadJsonWebToken, incurring a redundant payload parse and allocation.Encrypted (JWE) source tokens are rejected with a new
IDX14117message because their plaintext payload is not available for reuse.Changes
JsonWebToken(additive; existing parse paths unchanged).IDX14117log message for the JWE-source rejection.PublicAPI.Unshipped.txtupdated for the new public API.JsonWebTokenHeaderReplacementBenchmarksisolating the new constructor against reassemble-and-reparse.Benchmarks
Isolated microbenchmark (
Microsoft.IdentityModel.Benchmarks, net8.0, MediumRun, MemoryDiagnoser) — reassemble-and-reparse vs. the new constructor:The eliminated payload parse scales with payload size, so the win grows with the token.
Fixes #3529