Search code examples
c#json.netjwt.net-8.0

Strange behavior decoding JWT Token C# NET 8


I have this strange behavior while decoding a JWT bearer token.

As an example take this token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidmFyMSI6IjEiLCJ2YXIyIjoiMiIsInZhcjMiOiIzIiwidmFyNCI6IjQiLCJ2YXI1IjoiNSJ9.2VmaLH12HitYv8T7Z9wp6dRk87p9krZXFm3XOW9ulEM

This, pasted int jwt.io decodes as follows:
Header :

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload :

{
  "sub": "1234567890",
  "var1": "1",
  "var2": "2",
  "var3":"3",
  "var4":"4",
  "var5":"5"
}

Signature :

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  question1)

That said, let's get into the question: I've a big asp net core project, using net8, all nugets are up to date (Microsoft.AspNetCore.Authentication.JwtBearer 8.0.5, System.IdentityModel.Tokens.Jwt, Version=7.1.2.0)

If i paste these 3 lines into any part of the code

var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidmFyMSI6IjEiLCJ2YXIyIjoiMiIsInZhcjMiOiIzIiwidmFyNCI6IjQiLCJ2YXI1IjoiNSJ9.2VmaLH12HitYv8T7Z9wp6dRk87p9krZXFm3XOW9ulEM";
var tokenHandler = new JwtSecurityTokenHandler();
var tokenS = tokenHandler.ReadJwtToken(token);

I get only half of the claims! Half claims While if i use the same exact code on a fresh new project i get the expected result..

All claims

Could this be a problem of how the System.Text.Json library is configured ? Cause this is used inside the jwt handler.

I also went into debugging the dotnet code, trying to figure out where the problem was. I got into the JwtSecirutyToken.DecodeJws routing that calls JwtPayload.CreatePayload, here there is a while loop that reads the tokens read from the Utf8JsonReader, and it seems to be skipping all the odd tokens.

This is the snippet of the decompiled code (you can find the original one here: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/src/System.IdentityModel.Tokens.Jwt/JwtPayload.cs#L51)..

internal static JwtPayload CreatePayload(byte[] bytes, int length)
{
      JwtPayload payload = new JwtPayload();
      Utf8JsonReader reader = new Utf8JsonReader((ReadOnlySpan<byte>) bytes.AsSpan<byte>().Slice(0, length));
      if (!JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartObject, false))
        throw LogHelper.LogExceptionMessage((Exception) new JsonException(LogHelper.FormatInvariant("IDX11023: Expecting json reader to be positioned on '{0}', reader was positioned at: '{1}', Reading: '{2}', Position: '{3}', CurrentDepth: '{4}', BytesConsumed: '{5}'.", LogHelper.MarkAsNonPII((object) "JsonTokenType.StartObject"), LogHelper.MarkAsNonPII((object) reader.TokenType), LogHelper.MarkAsNonPII((object) "System.IdentityModel.Tokens.Jwt.JwtPayload"), LogHelper.MarkAsNonPII((object) reader.TokenStartIndex), LogHelper.MarkAsNonPII((object) reader.CurrentDepth), LogHelper.MarkAsNonPII((object) reader.BytesConsumed))));
      while (reader.Read())
      {
        if (reader.TokenType == JsonTokenType.PropertyName)
        {
        \\Continues with dotnet code ...
        }
      \\...
      }
\\...
}

And there i found that half of the claims is skipped because the condition

if (reader.TokenType == JsonTokenType.PropertyName)

is not met because the type is alternating between PropertyName and String, while on the project where this works, the type is always PropertyName and this seems to be the 'bug'.

I know that without the entire project the bug is not reproducible but i cannot share it now, i'm just hoping if anyone has experienced the same behavior.


Solution

  • As of this issue

    https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2546

    This is a bug due to a mismatch of nuget transitive versions

    Matching all the transitive package version to the same should solve the issue.