Skip to content

Commit

Permalink
Update reading string header properties.
Browse files Browse the repository at this point in the history
  • Loading branch information
pmaytak committed Jun 20, 2024
1 parent 7b968be commit aac0539
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ public partial class JsonWebToken
{
internal JsonClaimSet CreateHeaderClaimSet(byte[] bytes)
{
return CreateHeaderClaimSet(bytes.AsSpan());
return CreateHeaderClaimSet(bytes.AsMemory());
}

internal JsonClaimSet CreateHeaderClaimSet(byte[] bytes, int length)
{
return CreateHeaderClaimSet(bytes.AsSpan(0, length));
return CreateHeaderClaimSet(bytes.AsMemory(0, length));
}

internal JsonClaimSet CreateHeaderClaimSet(ReadOnlySpan<byte> byteSpan)
{
Utf8JsonReader reader = new(byteSpan);
internal JsonClaimSet CreateHeaderClaimSet(Memory<byte> tokenHeaderAsMemory)
{
Utf8JsonReader reader = new(tokenHeaderAsMemory.Span);
if (!JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.StartObject, true))
throw LogHelper.LogExceptionMessage(
new JsonException(
Expand All @@ -36,46 +36,19 @@ internal JsonClaimSet CreateHeaderClaimSet(ReadOnlySpan<byte> byteSpan)
LogHelper.MarkAsNonPII(reader.CurrentDepth),
LogHelper.MarkAsNonPII(reader.BytesConsumed))));

Dictionary<string, object> claims = new();
Dictionary<string, object> claims = [];
#if NET8_0_OR_GREATER
Dictionary<string, (int startIndex, int length)?> claimsBytes = [];
#endif
while (true)
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Alg))
{
_alg = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Alg, ClassName, true);
claims[JwtHeaderParameterNames.Alg] = _alg;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Cty))
{
_cty = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Cty, ClassName, true);
claims[JwtHeaderParameterNames.Cty] = _cty;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Kid))
{
_kid = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Kid, ClassName, true);
claims[JwtHeaderParameterNames.Kid] = _kid;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Typ))
{
_typ = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Typ, ClassName, true);
claims[JwtHeaderParameterNames.Typ] = _typ;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.X5t))
{
_x5t = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.X5t, ClassName, true);
claims[JwtHeaderParameterNames.X5t] = _x5t;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Zip))
{
_zip = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Zip, ClassName, true);
claims[JwtHeaderParameterNames.Zip] = _zip;
}
else
{
string propertyName = reader.GetString();
claims[propertyName] = JsonSerializerPrimitives.ReadPropertyValueAsObject(ref reader, propertyName, JsonClaimSet.ClassName, true);
}
#if NET8_0_OR_GREATER
ReadHeaderValue(ref reader, claims, claimsBytes, tokenHeaderAsMemory);
#else
ReadHeaderValue(ref reader, claims);
#endif
}
// We read a JsonTokenType.StartObject above, exiting and positioning reader at next token.
else if (JsonSerializerPrimitives.IsReaderAtTokenType(ref reader, JsonTokenType.EndObject, false))
Expand All @@ -84,7 +57,94 @@ internal JsonClaimSet CreateHeaderClaimSet(ReadOnlySpan<byte> byteSpan)
break;
};

#if NET8_0_OR_GREATER
return new JsonClaimSet(claims, claimsBytes, tokenHeaderAsMemory);
#else
return new JsonClaimSet(claims);
#endif
}

private protected virtual void ReadHeaderValue(ref Utf8JsonReader reader, IDictionary<string, object> claims)
{
_ = claims ?? throw LogHelper.LogArgumentNullException(nameof(claims));

if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Alg))
{
_alg = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Alg, ClassName, true);
claims[JwtHeaderParameterNames.Alg] = _alg;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Cty))
{
_cty = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Cty, ClassName, true);
claims[JwtHeaderParameterNames.Cty] = _cty;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Kid))
{
_kid = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Kid, ClassName, true);
claims[JwtHeaderParameterNames.Kid] = _kid;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Typ))
{
_typ = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Typ, ClassName, true);
claims[JwtHeaderParameterNames.Typ] = _typ;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.X5t))
{
_x5t = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.X5t, ClassName, true);
claims[JwtHeaderParameterNames.X5t] = _x5t;
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Zip))
{
_zip = JsonSerializerPrimitives.ReadString(ref reader, JwtHeaderParameterNames.Zip, ClassName, true);
claims[JwtHeaderParameterNames.Zip] = _zip;
}
else
{
string propertyName = reader.GetString();
claims[propertyName] = JsonSerializerPrimitives.ReadPropertyValueAsObject(ref reader, propertyName, JsonClaimSet.ClassName, true);
}
}

#if NET8_0_OR_GREATER
private protected virtual void ReadHeaderValue(
ref Utf8JsonReader reader,
Dictionary<string, object> claims,
Dictionary<string, (int startIndex, int length)?> claimsBytes,
Memory<byte> tokenAsMemory)
{
_ = claims ?? throw LogHelper.LogArgumentNullException(nameof(claims));
_ = claimsBytes ?? throw LogHelper.LogArgumentNullException(nameof(claimsBytes));

if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Alg))
{
claimsBytes[JwtHeaderParameterNames.Alg] = JsonSerializerPrimitives.ReadStringBytesLocation(ref reader, tokenAsMemory, JwtRegisteredClaimNames.Alg, ClassName, true);
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Cty))
{
claimsBytes[JwtHeaderParameterNames.Cty] = JsonSerializerPrimitives.ReadStringBytesLocation(ref reader, tokenAsMemory, JwtHeaderParameterNames.Cty, ClassName, true);
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Kid))
{
claimsBytes[JwtHeaderParameterNames.Kid] = JsonSerializerPrimitives.ReadStringBytesLocation(ref reader, tokenAsMemory, JwtHeaderParameterNames.Kid, ClassName, true);
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Typ))
{
claimsBytes[JwtHeaderParameterNames.Typ] = JsonSerializerPrimitives.ReadStringBytesLocation(ref reader, tokenAsMemory, JwtHeaderParameterNames.Typ, ClassName, true);
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.X5t))
{
claimsBytes[JwtHeaderParameterNames.X5t] = JsonSerializerPrimitives.ReadStringBytesLocation(ref reader, tokenAsMemory, JwtHeaderParameterNames.X5t, ClassName, true);
}
else if (reader.ValueTextEquals(JwtHeaderUtf8Bytes.Zip))
{
claimsBytes[JwtHeaderParameterNames.Zip] = JsonSerializerPrimitives.ReadStringBytesLocation(ref reader, tokenAsMemory, JwtHeaderParameterNames.Zip, ClassName, true);
}
else
{
string propertyName = reader.GetString();
claims[propertyName] = JsonSerializerPrimitives.ReadPropertyValueAsObject(ref reader, propertyName, JsonClaimSet.ClassName, true);
}
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ internal JsonClaimSet CreatePayloadClaimSet(Memory<byte> tokenPayloadAsMemory)

private protected virtual void ReadPayloadValue(ref Utf8JsonReader reader, IDictionary<string, object> claims)
{
_ = claims ?? throw LogHelper.LogArgumentNullException(nameof(claims));

if (reader.ValueTextEquals(JwtPayloadUtf8Bytes.Aud))
{
_audiences = [];
Expand Down Expand Up @@ -136,6 +138,9 @@ private protected virtual void ReadPayloadValue(
Dictionary<string, (int startIndex, int length)?> claimsBytes,
Memory<byte> tokenAsMemory)
{
_ = claims ?? throw LogHelper.LogArgumentNullException(nameof(claims));
_ = claimsBytes ?? throw LogHelper.LogArgumentNullException(nameof(claimsBytes));

if (reader.ValueTextEquals(JwtPayloadUtf8Bytes.Aud))
{
_audiences = [];
Expand Down
89 changes: 34 additions & 55 deletions src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ internal void ReadToken(ReadOnlyMemory<char> encodedTokenMemory)

try
{
Header = CreateHeaderClaimSet(Base64UrlEncoder.Decode(headerSpan).AsSpan());
Header = CreateHeaderClaimSet(Base64UrlEncoder.Decode(headerSpan).AsMemory());
}
catch (Exception ex)
{
Expand Down Expand Up @@ -572,7 +572,7 @@ internal JsonClaimSet CreateClaimSet(ReadOnlySpan<char> strSpan, int startIndex,

byte[] output = new byte[outputSize];
Base64UrlEncoder.Decode(strSpan.Slice(startIndex, length), output);
return createHeaderClaimSet ? CreateHeaderClaimSet(output.AsSpan()) : CreatePayloadClaimSet(output.AsMemory());
return createHeaderClaimSet ? CreateHeaderClaimSet(output.AsMemory()) : CreatePayloadClaimSet(output.AsMemory());
}

/// <summary>
Expand Down Expand Up @@ -1129,64 +1129,43 @@ internal DateTime? ValidToNullable
}
#endregion

#region Payload Properties Bytes
#if NET8_0_OR_GREATER

/// <summary>
/// Gets the 'azp' claim from the payload.
/// </summary>
/// <remarks>
/// Identifies the authorized party for the id_token.
/// see: https://openid.net/specs/openid-connect-core-1_0.html
/// <para>
/// If the 'azp' claim is not found, an empty string is returned.
/// </para>
/// </remarks>
public ReadOnlySpan<byte> AzpBytes
{
get
{
return Payload.GetStringBytesValue(JwtRegisteredClaimNames.Azp);
}
}
#region Header Properties Bytes

/// <summary>
/// Gets the 'value' of the 'jti' claim from the payload.
/// </summary>
/// <remarks>
/// Provides a unique identifier for the JWT.
/// see: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7
/// <para>
/// If the 'jti' claim is not found, an empty string is returned.
/// </para>
/// </remarks>
public ReadOnlySpan<byte> IdBytes
{
get
{
return Payload.GetStringBytesValue(JwtRegisteredClaimNames.Jti);
}
}
/// <inheritdoc cref="Alg" />
public ReadOnlySpan<byte> AlgBytes => Payload.GetStringBytesValue(JwtHeaderParameterNames.Alg);

/// <summary>
/// Gets the 'value' of the 'iss' claim from the payload.
/// </summary>
/// <remarks>
/// Identifies the principal that issued the JWT.
/// see: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
/// <para>
/// If the 'iss' claim is not found, an empty string is returned.
/// </para>
/// </remarks>
public ReadOnlySpan<byte> IssuerBytes
{
get
{
return Payload.GetStringBytesValue(JwtRegisteredClaimNames.Iss);
}
}
/// <inheritdoc cref="Cty" />
public ReadOnlySpan<byte> CtyBytes => Payload.GetStringBytesValue(JwtHeaderParameterNames.Cty);

/// <inheritdoc cref="Kid" />
public ReadOnlySpan<byte> KidBytes => Payload.GetStringBytesValue(JwtHeaderParameterNames.Kid);

/// <inheritdoc cref="Typ" />
public ReadOnlySpan<byte> TypBytes => Payload.GetStringBytesValue(JwtHeaderParameterNames.Typ);

/// <inheritdoc cref="X5t" />
public ReadOnlySpan<byte> X5tBytes => Payload.GetStringBytesValue(JwtHeaderParameterNames.X5t);

/// <inheritdoc cref="Zip" />
public ReadOnlySpan<byte> ZipBytes => Payload.GetStringBytesValue(JwtHeaderParameterNames.Zip);

#endif
#endregion

#region Payload Properties Bytes

/// <inheritdoc cref="Azp" />
public ReadOnlySpan<byte> AzpBytes => Payload.GetStringBytesValue(JwtRegisteredClaimNames.Azp);

/// <inheritdoc cref="Id" />
public ReadOnlySpan<byte> IdBytes => Payload.GetStringBytesValue(JwtRegisteredClaimNames.Jti);

/// <inheritdoc cref="Issuer" />
public ReadOnlySpan<byte> IssuerBytes => Payload.GetStringBytesValue(JwtRegisteredClaimNames.Iss);

#endregion

#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1540,7 +1540,7 @@ public void ParseToken_WithByteProperties()
{ JwtRegisteredClaimNames.Aud, Default.Audience },
{ JwtRegisteredClaimNames.Azp, escapedAzp },
{ JwtRegisteredClaimNames.Jti, Default.Jti },
{ "uknown_claim", "unknown_claim_value" },
{ "unknown_claim", "unknown_claim_value" },
}
};

Expand Down

0 comments on commit aac0539

Please sign in to comment.