diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs index d98a916982..c32e920f81 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonWebToken.PayloadClaimSet.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Text; using System.Text.Json; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; @@ -78,8 +79,10 @@ internal JsonClaimSet CreatePayloadClaimSet(ReadOnlySpan byteSpan) } else if (reader.ValueTextEquals(JwtPayloadUtf8Bytes.Iss)) { - _iss = JsonSerializerPrimitives.ReadString(ref reader, JwtRegisteredClaimNames.Iss, ClassName, true); - claims[JwtRegisteredClaimNames.Iss] = _iss; + + IssuerUtf8 = JsonSerializerPrimitives.ReadStringUtf8(ref reader, JwtRegisteredClaimNames.Jti, ClassName, true).ToArray(); + claims[JwtRegisteredClaimNames.Iss] = Encoding.UTF8.GetString(IssuerUtf8.ToArray()); + reader.Read(); } else if (reader.ValueTextEquals(JwtPayloadUtf8Bytes.Jti)) { diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs index b7b66a3596..a4dd812f60 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebToken.cs @@ -53,7 +53,6 @@ public partial class JsonWebToken : SecurityToken internal long? _iat; internal DateTime? _iatDateTime; internal string _id; - internal string _iss; internal string _jti; internal string _sub; internal long? _nbf; @@ -1031,14 +1030,9 @@ public DateTime IssuedAt /// If the 'iss' claim is not found, an empty string is returned. /// /// - public override string Issuer - { - get - { - _iss ??= Payload.GetStringValue(JwtRegisteredClaimNames.Iss); - return _iss; - } - } + public override string Issuer => Encoding.UTF8.GetString(IssuerUtf8.ToArray()); + + internal ReadOnlyMemory IssuerUtf8 { get; set; } /// /// Gets the 'value' of the 'jti' claim from the payload. diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs index 3d7882e8d6..5c38810402 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs @@ -845,7 +845,7 @@ private async ValueTask ValidateTokenPayloadAsync(JsonWeb Validators.ValidateLifetime(notBefore, expires, jsonWebToken, validationParameters); Validators.ValidateAudience(jsonWebToken.Audiences, jsonWebToken, validationParameters); - string issuer = await Validators.ValidateIssuerAsync(jsonWebToken.Issuer, jsonWebToken, validationParameters, configuration).ConfigureAwait(false); + string issuer = await Validators.ValidateIssuerAsync(jsonWebToken.IssuerUtf8, jsonWebToken, validationParameters, configuration).ConfigureAwait(false); Validators.ValidateTokenReplay(expires, jsonWebToken.EncodedToken, validationParameters); if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jsonWebToken.Actor)) diff --git a/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs b/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs index 6f25f80c54..89f69d993c 100644 --- a/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs +++ b/src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Text; using System.Text.Json.Serialization; namespace Microsoft.IdentityModel.Tokens @@ -12,10 +14,22 @@ namespace Microsoft.IdentityModel.Tokens /// public abstract class BaseConfiguration { + private string _issuer; + /// /// Gets the issuer specified via the metadata endpoint. /// - public virtual string Issuer { get; set; } + public virtual string Issuer + { + get => _issuer; + set + { + _issuer = value; + IssuerUtf8 = value != null ? Encoding.UTF8.GetBytes(value) : null; + } + } + + internal ReadOnlyMemory IssuerUtf8 { get; private set; } /// /// Gets the that the IdentityProvider indicates are to be used in order to sign tokens. diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs index cd99a503ca..81d8528af7 100644 --- a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs @@ -644,6 +644,19 @@ internal static string ReadString(ref Utf8JsonReader reader, string propertyName return retval; } + internal static ReadOnlySpan ReadStringUtf8(ref Utf8JsonReader reader, string propertyName, string className, bool read = false) + { + // returning null keeps the same logic as JsonSerialization.ReadObject + if (IsReaderPositionedOnNull(ref reader, read, true)) + return null; + + if (!IsReaderAtTokenType(ref reader, JsonTokenType.String, false)) + throw LogHelper.LogExceptionMessage( + CreateJsonReaderExceptionInvalidType(ref reader, "JsonTokenType.StartArray", className, propertyName)); + + return reader.ValueSpan; + } + internal static string ReadStringAsBool(ref Utf8JsonReader reader, string propertyName, string className, bool read = false) { // The parameter 'read' can be used by callers reader position the reader to the next token. diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs index f110a31e45..5cce475420 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Security.Claims; +using System.Text; using System.Threading.Tasks; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; @@ -893,11 +894,23 @@ public string RoleClaimType /// public IEnumerable ValidAudiences { get; set; } + private string _validIssuer; + /// /// Gets or sets a that represents a valid issuer that will be used to check against the token's issuer. /// The default is null. /// - public string ValidIssuer { get; set; } + public string ValidIssuer + { + get => _validIssuer; + set + { + _validIssuer = value; + ValidIssuerUtf8 = value != null ? Encoding.UTF8.GetBytes(value) : null; + } + } + + internal ReadOnlyMemory ValidIssuerUtf8 { get; private set; } /// /// Gets or sets the that contains valid issuers that will be used to check against the token's issuer. diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 5bb7e07530..1c0392086f 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading.Tasks; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; @@ -219,7 +220,7 @@ public static string ValidateIssuer(string issuer, SecurityToken securityToken, /// An EXACT match is required. internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) { - ValueTask vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration); + ValueTask vt = ValidateIssuerAsync(Encoding.UTF8.GetBytes(issuer), securityToken, validationParameters, configuration); return vt.IsCompletedSuccessfully ? vt.Result : vt.AsTask().GetAwaiter().GetResult(); @@ -228,7 +229,7 @@ internal static string ValidateIssuer(string issuer, SecurityToken securityToken /// /// Determines if an issuer found in a is valid. /// - /// The issuer to validate + /// The issuer to validate /// The that is being validated. /// required for validation. /// The required for issuer and signing key validation. @@ -240,7 +241,7 @@ internal static string ValidateIssuer(string issuer, SecurityToken securityToken /// If 'issuer' failed to matched either or one of or . /// An EXACT match is required. internal static async ValueTask ValidateIssuerAsync( - string issuer, + ReadOnlyMemory issuerUtf8, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) @@ -248,9 +249,12 @@ internal static async ValueTask ValidateIssuerAsync( if (validationParameters == null) throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + string issuer = Encoding.UTF8.GetString(issuerUtf8.ToArray()); + if (validationParameters.IssuerValidatorAsync != null) return await validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters).ConfigureAwait(false); + if (validationParameters.IssuerValidatorUsingConfiguration != null) return validationParameters.IssuerValidatorUsingConfiguration(issuer, securityToken, validationParameters, configuration); @@ -276,7 +280,7 @@ internal static async ValueTask ValidateIssuerAsync( if (configuration != null) { - if (string.Equals(configuration.Issuer, issuer)) + if (string.Equals(configuration.IssuerUtf8, issuerUtf8)) { if (LogHelper.IsEnabled(EventLogLevel.Informational)) LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); @@ -285,7 +289,7 @@ internal static async ValueTask ValidateIssuerAsync( } } - if (string.Equals(validationParameters.ValidIssuer, issuer)) + if (validationParameters.ValidIssuerUtf8.Equals(issuerUtf8)) { if (LogHelper.IsEnabled(EventLogLevel.Informational)) LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer));