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

Replace JsonWebToken ReadPayloadValue with a delegate #2981

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from

Conversation

pmaytak
Copy link
Contributor

@pmaytak pmaytak commented Nov 5, 2024

Fixes #2982

This pull request introduces several updates and improvements to the Microsoft.IdentityModel.JsonWebTokens library, focusing on enhancing the handling of token payloads and custom claims. The most important changes include adding new constructors to the JsonWebToken class, updating the JsonClaimSet initialization, and introducing a new delegate for reading custom token payload values.

Enhancements to JsonWebToken:

  • Added new constructors to the JsonWebToken class to support initializing tokens with custom delegates for reading token payload values. (src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs) [1] [2] [3]
  • Introduced the ReadTokenPayloadValueDelegates property to the JsonWebToken class, allowing custom handling of specific claim names during token reading. (src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs)

Updates to TokenValidationParameters:

  • Added the ReadTokenPayloadValueDelegates property to the TokenValidationParameters class to support custom claim handling during token validation. (src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs) [1] [2]
  • Updated the copy constructor of TokenValidationParameters to include the new ReadTokenPayloadValueDelegates property. (src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs)

Refactoring and cleanup:

  • Refactored the ReadPayloadValue method in JsonWebToken.PayloadClaimSet to simplify the handling of standard claims and integrate custom delegate handling. (src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs) [1] [2]
  • Removed the CustomJsonWebToken class from the tests, as its functionality is now covered by the new delegate-based approach. (test/Microsoft.IdentityModel.JsonWebTokens.Tests/CustomJsonWebToken.cs)

Delegate introduction:

  • Introduced the ReadTokenPayloadValueDelegate delegate to handle custom claim reading during token payload processing. (src/Microsoft.IdentityModel.Tokens/Delegates.cs)

@pmaytak pmaytak changed the title Replace JsonWebToken read overload method with delegates to read token values. Replace JsonWebToken ReadPayloadValue with a delegate Nov 6, 2024
@pmaytak
Copy link
Contributor Author

pmaytak commented Nov 7, 2024

Edit: These results are outdated.

Looks like when using delegates there're extra allocations because of:

string claimName = reader.GetString();
claims[claimName] = ReadTokenPayloadValueDelegate(ref reader, claimName);
Delegates Method Mean Ratio Error StdDev P90 P95 P100 Gen0 Allocated Alloc Ratio
Without JsonWebTokenHandler_ValidateTokenAsyncWithTVP 30.72 μs 1.000 0.026 μs 0.055 μs 30.79 μs 30.81 μs 30.89 μs 0.2441 7.23 KB 1.00
With JsonWebTokenHandler_ValidateTokenAsyncWithTVP 30.66 μs 0.998 0.124 μs 0.273 μs 30.96 μs 31.02 μs 31.10 μs 0.3052 7.55 KB 1.044
Delegates Method Mean Ratio Error StdDev P90 P95 P100 Gen0 Allocated Alloc Ratio
Without ReadJWS_FromMemory 8.373 μs 1.00 0.0331 μs 0.0727 μs 8.465 μs 8.499 μs 8.530 μs 0.2289 5.84 KB 1.00
With ReadJWS_FromMemory 8.201 μs 0.979 0.0287 μs 0.0636 μs 8.295 μs 8.313 μs 8.357 μs 0.2441 6.07 KB 1.039

@pmaytak pmaytak requested a review from Copilot January 8, 2025 08:35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 9 changed files in this pull request and generated no comments.

Files not reviewed (4)
  • src/Microsoft.IdentityModel.JsonWebTokens/InternalAPI.Unshipped.txt: Language not supported
  • src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt: Language not supported
  • test/Microsoft.IdentityModel.JsonWebTokens.Tests/CustomJsonWebToken.cs: Evaluated as low risk
  • src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs: Evaluated as low risk
Comments suppressed due to low confidence (1)

src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs:31

  • The initialization of _jsonClaims should be 'new Dictionary<string, object>()' instead of '[]'.
_jsonClaims = [];
@pmaytak
Copy link
Contributor Author

pmaytak commented Jan 15, 2025

Edit: These results are outdated.

Updated results comparing delegates which have a dictionary as a parameter. Ran JsonWebTokenHandler_ValidateTokenAsyncWithTVP and ReadJWS_FromMemory with extended claims. These test using the default delegate implemenation.

Delegates Method Mean Ratio Error StdDev P90 P95 P100 Gen0 Allocated Alloc Ratio
Without JsonWebTokenHandler_ValidateTokenAsyncWithTVP 30.13 μs 1.00 0.169 μs 0.374 μs 30.61 μs 30.74 μs 31.07 μs 0.2441 7.23 KB 1.00
With JsonWebTokenHandler_ValidateTokenAsyncWithTVP 29.34 μs 0.974 0.085 μs 0.188 μs 29.59 μs 29.61 μs 29.87 μs 0.2441 7.33 KB 1.014
Delegates Method Mean Ratio Error StdDev P90 P95 P100 Gen0 Allocated Alloc Ratio
Without ReadJWS_FromMemory 7.523 μs 1.00 0.0333 μs 0.0724 μs 7.599 μs 7.612 μs 7.672 μs 0.2365 5.84 KB 1.00
With ReadJWS_FromMemory 7.059 μs 0.938 0.0416 μs 0.0905 μs 7.160 μs 7.170 μs 7.205 μs 0.2365 5.84 KB 1.00

BenchmarkDotNet v0.13.12, Windows 11 (10.0.26100.2605) (Hyper-V)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.101
[Host] : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
MediumRun : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI

@pmaytak pmaytak marked this pull request as ready for review February 11, 2025 08:46
@pmaytak pmaytak requested a review from a team as a code owner February 11, 2025 08:46
Copy link

Summary

Summary
Generated on: 2/11/2025 - 8:47:25 AM
Coverage date: 2/11/2025 - 8:37:55 AM - 2/11/2025 - 8:47:00 AM
Parser: MultiReport (60x Cobertura)
Assemblies: 1
Classes: 7
Files: 2
Line coverage: 80.3% (620 of 772)
Covered lines: 620
Uncovered lines: 152
Coverable lines: 772
Total lines: 483
Branch coverage: 67.8% (228 of 336)
Covered branches: 228
Total branches: 336
Method coverage: Feature is only available for sponsors

Coverage

Microsoft.IdentityModel.JsonWebTokens - 80.3%
Name Line Branch
Microsoft.IdentityModel.JsonWebTokens 80.3% 67.8%
Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities 100%
System.Text.RegularExpressions.Generated 80.3% 67.8%
System.Text.RegularExpressions.Generated 80.3% 67.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F12A1AEDDDFE32BA
DF4DBFF323AF1BCB48B9F9721B7CD3E05F5E034CF225E3DF8__CreateJweRegex_1
79.2% 68%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F12A1AEDDDFE32BA
DF4DBFF323AF1BCB48B9F9721B7CD3E05F5E034CF225E3DF8__CreateJwsRegex_0
81.4% 67.6%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F334844C618E00D3
CEC5D3FE0D00CF0141BBEE98635313BB2CB8D3921464CE05A__CreateJweRegex_1
79.2% 68%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F334844C618E00D3
CEC5D3FE0D00CF0141BBEE98635313BB2CB8D3921464CE05A__CreateJwsRegex_0
81.4% 67.6%

Copy link

Summary

Summary
Generated on: 2/11/2025 - 9:00:21 AM
Coverage date: 2/11/2025 - 8:50:12 AM - 2/11/2025 - 8:59:54 AM
Parser: MultiReport (60x Cobertura)
Assemblies: 1
Classes: 7
Files: 2
Line coverage: 80.3% (620 of 772)
Covered lines: 620
Uncovered lines: 152
Coverable lines: 772
Total lines: 483
Branch coverage: 67.8% (228 of 336)
Covered branches: 228
Total branches: 336
Method coverage: Feature is only available for sponsors

Coverage

Microsoft.IdentityModel.JsonWebTokens - 80.3%
Name Line Branch
Microsoft.IdentityModel.JsonWebTokens 80.3% 67.8%
Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities 100%
System.Text.RegularExpressions.Generated 80.3% 67.8%
System.Text.RegularExpressions.Generated 80.3% 67.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F12A1AEDDDFE32BA
DF4DBFF323AF1BCB48B9F9721B7CD3E05F5E034CF225E3DF8__CreateJweRegex_1
79.2% 68%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F12A1AEDDDFE32BA
DF4DBFF323AF1BCB48B9F9721B7CD3E05F5E034CF225E3DF8__CreateJwsRegex_0
81.4% 67.6%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F334844C618E00D3
CEC5D3FE0D00CF0141BBEE98635313BB2CB8D3921464CE05A__CreateJweRegex_1
79.2% 68%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F334844C618E00D3
CEC5D3FE0D00CF0141BBEE98635313BB2CB8D3921464CE05A__CreateJwsRegex_0
81.4% 67.6%

}
}
}
else if (reader.ValueTextEquals(JwtPayloadUtf8Bytes.Azp))
Copy link
Member

@brentschmaltz brentschmaltz Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user will have no ability to parse any of the claims that are parsed before calling the delegate.

There are a couple of things we can do.

  • Have a model where a power user can take total control.

  • Call the delegate if hook has been added for 'aud' or any of the internal types.
    in this case we could create a u8 byte array for the claim type associated with a delegate so that reader.ValueTextEquals performant check could be used.

@brentschmaltz
Copy link
Member

It would be great to have the same design when reading the header.

Copy link
Member

@brentschmaltz brentschmaltz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a way to allow the user to parse all claims in a performant manner.
This design skips claims that are parsed.

Allowing the user to parse all claims allows for an implementation where validation could fail fast.

Failing fast needs to have a design that can propagate a detailed error result to upper layers for error reporting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Replace JsonWebToken ReadPayloadValue with a delegate
2 participants