Skip to content

Conversation

@jaywang-cn
Copy link

Background

Currently, the method GetStringClaimValueType returns ClaimValueTypes.DateTime only if the input string matches exactly the output of DateTime.ToUniversalTime().ToString("o", ...).
This leads to inconsistent behavior for ISO8601 UTC strings with or without trailing zeros, e.g.:

  • "2025-11-26T07:18:45.1234560Z" → DateTime
  • "2025-11-26T07:18:45.123456Z" → String

Fix

With this change, all strings that can be successfully parsed as a valid DateTime will return ClaimValueTypes.DateTime, regardless of trailing zeroes.

Impact

This fix ensures that semantically equivalent ISO8601 UTC time strings are consistently recognized as DateTime claims.

Tests

  • Added/Updated tests for ISO8601 datetime strings with and without trailing zeros.

@jaywang-cn jaywang-cn requested a review from a team as a code owner November 26, 2025 09:58
@jaywang-cn
Copy link
Author

jaywang-cn commented Nov 26, 2025

@microsoft-github-policy-service agree company="WiseTech Global"

1 similar comment
@jaywang-cn
Copy link
Author

@microsoft-github-policy-service agree company="WiseTech Global"

@jaywang-cn
Copy link
Author

@pmaytak

@pmaytak pmaytak requested a review from Copilot December 3, 2025 16:53
Copilot finished reviewing on behalf of pmaytak December 3, 2025 16:56
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes DateTime claim value type recognition for ISO8601 strings by removing the exact string match requirement, allowing all parseable DateTime strings to be classified as DateTime claims rather than just those with specific trailing zero patterns.

Key Changes:

  • Modified GetStringClaimValueType to use DateTime.TryParse with DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal flags instead of requiring exact string matching
  • Removed the logic that converted parsed DateTime back to string for comparison
  • Added tests for ISO8601 strings with and without trailing zeros

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs Simplified DateTime detection logic to accept any parseable DateTime string instead of requiring exact format match
test/Microsoft.IdentityModel.JsonWebTokens.Tests/JwtTokenUtilitiesTests.cs Added test verifying trailing zeros are handled correctly in ISO8601 strings
test/System.IdentityModel.Tokens.Jwt.Tests/JwtPayloadTest.cs Added test verifying JwtPayload handles trailing zeros correctly in ISO8601 strings

return ClaimValueTypes.String;

if (DateTime.TryParse(str, out DateTime dateTimeValue))
if (DateTime.TryParse(str, null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out _))
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The change to use DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal is broader than just handling trailing zeros. This will now classify ANY parseable DateTime string as a DateTime claim, including:

  • Date-only strings like "2019-11-15" (previously String, now DateTime)
  • Ambiguous formats like "01/02/2023"
  • Relative date strings that DateTime.TryParse might accept

This is a significant behavioral change beyond the trailing zeros issue described in the PR. Consider:

  1. Adding comprehensive tests for various date formats to document the new behavior
  2. Verifying this doesn't break existing applications that expect certain date strings to remain as String type
  3. Evaluating if DateTimeStyles.RoundtripKind (used in JsonSerializerPrimitives.cs line 151) would be more appropriate to maintain consistency and only accept ISO8601 roundtrip formats
Suggested change
if (DateTime.TryParse(str, null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out _))
if (DateTime.TryParseExact(str, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out _))

Copilot uses AI. Check for mistakes.
Comment on lines +224 to +233
[Fact]
public void GetStringClaimValueType_ReturnsDateTime_ForIso8601WithOrWithoutTrailingZeros()
{
var iso8601WithTrailingZeros = "2025-11-26T09:30:45.1234560Z";
var iso8601WithoutTrailingZeros = "2025-11-26T09:30:45.123456Z";
var resultWithTrailingZeros = JwtTokenUtilities.GetStringClaimValueType(iso8601WithTrailingZeros);
var resultWithoutTrailingZeros = JwtTokenUtilities.GetStringClaimValueType(iso8601WithoutTrailingZeros);
Assert.Equal(ClaimValueTypes.DateTime, resultWithTrailingZeros);
Assert.Equal(ClaimValueTypes.DateTime, resultWithoutTrailingZeros);
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Consider adding test cases for edge cases beyond trailing zeros, such as:

  • Date-only strings: "2019-11-15"
  • Various ISO8601 formats: "2019-11-15T14:31:21Z", "2019-11-15T14:31:21+00:00"
  • Non-ISO8601 formats that DateTime.TryParse accepts: "11/15/2019", "Nov 15, 2019"
  • Strings that should remain as String type

This would document the new behavior comprehensively and ensure the change works as intended across different scenarios.

Copilot uses AI. Check for mistakes.
Comment on lines +186 to +201
[Fact]
public void TestReturnsDateTime_ForIso8601WithOrWithoutTrailingZeros()
{
var iso8601WithTrailingZeros = "2025-11-26T09:30:45.1234560Z";
var iso8601WithoutTrailingZeros = "2025-11-26T09:30:45.123456Z";
var jwtpayload = new JwtPayload
{
{ "dateWithTrailingZeros", iso8601WithTrailingZeros },
{ "dateWithoutTrailingZeros", iso8601WithoutTrailingZeros }
};

var claimWithTrailingZeros = jwtpayload.Claims.First(c => c.Type == "dateWithTrailingZeros");
var claimWithoutTrailingZeros = jwtpayload.Claims.First(c => c.Type == "dateWithoutTrailingZeros");
Assert.Equal(ClaimValueTypes.DateTime, claimWithTrailingZeros.ValueType);
Assert.Equal(ClaimValueTypes.DateTime, claimWithoutTrailingZeros.ValueType);
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Consider adding test cases for edge cases beyond trailing zeros, such as:

  • Date-only strings: "2019-11-15"
  • Various ISO8601 formats with different timezone indicators
  • Non-ISO8601 formats that DateTime.TryParse accepts
  • Strings that should remain as String type

This would document the new behavior comprehensively and ensure the change works as intended across different scenarios.

Copilot uses AI. Check for mistakes.
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.

1 participant