Skip to content

Conversation

@henrikwidlund
Copy link

@henrikwidlund henrikwidlund commented May 7, 2025

Add Memory based overload for CanReadToken

  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • If any gains or losses in performance are possible, you've included benchmarks for your changes. More info
  • There's an open issue for the PR that you are making. If you'd like to propose a new feature or change, please open an issue to discuss the change or find an existing issue.

Description

Added an overload for the CanReadToken in JsonWebTokenHandler and JwtSecurityTokenHandler with ReadOnlyMemory<char> to reduce allocations and adjusted the string based overload to call the new method.

New overload is only available on dotnet 8 and above as the span based regex methods used to match JWS/JWE isn't available in dotnet 6.

Fixes #3221

@henrikwidlund henrikwidlund requested a review from a team as a code owner May 7, 2025 08:29
@henrikwidlund
Copy link
Author

@microsoft-github-policy-service agree

@henrikwidlund
Copy link
Author

I did not add benchmarks, even though this is a performance improvement. The reason being that the string based API calls the span based one.

Change to ReadOnlyMemory to reduce allocations when calling ToString
@kllysng
Copy link
Contributor

kllysng commented May 8, 2025

Thanks for your contribution @henrikwidlund . Are you planning to add unit tests?

@henrikwidlund
Copy link
Author

@kllysng I was thinking that the regular string based ones would cover it since the string based method calls the memory based one. Let me know if you want explicit tests.

@henrikwidlund
Copy link
Author

I've added tests now either way. Also added the method to JwtSecurityTokenHandler. Was thinking of adding the memory based overload to JwtSecurityTokenHandler.ReadToken, but feels out of scope.

@henrikwidlund henrikwidlund changed the title Add Span based overload for CanReadToken Add ReadOnlyMemory based overload for CanReadToken May 9, 2025
// more segments than the maximum we know how to handle.
int segmentCount = JwtTokenUtilities.CountJwtTokenPart(token.Span, JwtConstants.MaxJwtSegmentCount + 1);

switch (segmentCount)
Copy link
Contributor

@brentschmaltz brentschmaltz May 12, 2025

Choose a reason for hiding this comment

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

this code (switch (segmentCount)) can be shared between JsonWebTokenHandler and JwtSecurityTokenHandler.

When you think about it, most of this code could be shared between JsonWebTokenHandler and JwtSecurityTokenHandler.

Copy link
Author

Choose a reason for hiding this comment

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

@brentschmaltz please resolve if you're satisfied with the solution :)

Copy link
Contributor

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

refactor and share common code between JsonWebTokenHandler and JwtSecurityTokenHandler.

@henrikwidlund
Copy link
Author

Good points @brentschmaltz, I've applied your suggestions.

Copy link
Contributor

@sruke sruke left a comment

Choose a reason for hiding this comment

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

LGTM, thank you for taking the suggestions and making the contribution @henrikwidlund.

@henrikwidlund
Copy link
Author

I see that a few unit tests are failing because of accessibility. I'll make the method internal again tomorrow

@henrikwidlund
Copy link
Author

@sruke fixed the compiler error

@sruke
Copy link
Contributor

sruke commented May 15, 2025

@henrikwidlund, a few unit tests are still failing. We’re planning to release IdentityModel today—would it be okay to include this PR in the next release (in a month) instead?

@henrikwidlund
Copy link
Author

@sruke I believe I've fixed the test (was a real bug). I leave it up to you if it's getting to tight for your deadline (although, it would be nice to have it :))

@henrikwidlund
Copy link
Author

I noticed that the PR build is still failing, but I don't understand if it's related to my code changes?

@sruke
Copy link
Contributor

sruke commented May 20, 2025

I noticed that the PR build is still failing, but I don't understand if it's related to my code changes?

The failure (Resource not accessible by integration) appears unrelated to recent changes. All tests passed. I’m rerunning the build to check if the issue resolves. This change wasn’t included in the previous IdentityModel release but will be part of the upcoming one.

@henrikwidlund
Copy link
Author

I noticed that the PR build is still failing, but I don't understand if it's related to my code changes?

The failure (Resource not accessible by integration) appears unrelated to recent changes. All tests passed. I’m rerunning the build to check if the issue resolves. This change wasn’t included in the previous IdentityModel release but will be part of the upcoming one.

Notice that it didn't help 😕

@henrikwidlund
Copy link
Author

@sruke @brentschmaltz ping, any ideas what the problem is and when this can be merged?

…ndler_memory

# Conflicts:
#	src/Microsoft.IdentityModel.JsonWebTokens/PublicAPI.Unshipped.txt
@henrikwidlund
Copy link
Author

@sruke @brentschmaltz ping again :/

/// <param name="maxCount">The maximum number of segments to count up to.</param>
/// <returns>The number of segments up to <paramref name="maxCount"/>.</returns>
internal static int CountJwtTokenPart(string token, int maxCount)
internal static int CountJwtTokenPart(ReadOnlySpan<char> token, int maxCount)
Copy link
Contributor

@brentschmaltz brentschmaltz Jul 1, 2025

Choose a reason for hiding this comment

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

Why do we need this as ReadOnlySpan?

We don't like to remove the old method as that can cause issues when mixed versions are used.
We end up with a MethodNotFound exception

Copy link
Author

@henrikwidlund henrikwidlund Jul 2, 2025

Choose a reason for hiding this comment

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

We need it because without it the PR would not make sense as we'd need to work with strings again. If you want I can create a method that takes string and calls this one.

Copy link
Contributor

Choose a reason for hiding this comment

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

@henrikwidlund i was thinking why ReadOnlySpan and not ReadOnlyMemory?

Copy link
Author

Choose a reason for hiding this comment

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

Only reasons are passing a smaller object and that that's what the method works with. If that's what you're thinking about I don't understand your comment about method not found as that would happen regardless.

Choose a reason for hiding this comment

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

@brentschmaltz I believe older versions of .NET does not support regular expressions for ReadOnlySpan, and this library targets older versions of .NET. This is the reason ReadOnlyMemory is used in the CanReadToken method.

ReadOnlySpan is more efficient and what libraries should use/support whenever possible.

I agreee that supporting string would be useful, even if it is basically just a wrapper for the new method.

Copy link
Author

Choose a reason for hiding this comment

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

So what's your suggestion, add back the string based one and have that one call the span one, or close the PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

So what's your suggestion, add back the string based one and have that one call the span one, or close the PR?

Yes, that is what we are thinking.

Copy link
Author

Choose a reason for hiding this comment

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

Which of them? I can do both, but it would be a bit of a waste 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

add back the string based one and have that one call the span one,

Copy link
Author

Choose a reason for hiding this comment

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

Okay, done :)

@henrikwidlund
Copy link
Author

henrikwidlund commented Sep 23, 2025

Is this PR still wanted? It's been open for four months now. @brentschmaltz @sruke @jennyf19

@pmaytak
Copy link
Contributor

pmaytak commented Sep 26, 2025

@henrikwidlund Sorry about the delay. I'm on holiday next week. I'll bring this up with the team, if there's available bandwidth; otherwise, I'll follow up on this after I'm back.

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] ReadOnlySpan based overload for JsonWebTokenHandler.CanReadToken

6 participants