Skip to content

Commit

Permalink
JsonWebTokenHandler IssuerExtensibility (#2987)
Browse files Browse the repository at this point in the history
* JsonWebTokenHandler IssuerExtensibility

* Address PR Comments

---------

Co-authored-by: id4s <user@contoso.com>
  • Loading branch information
brentschmaltz and HP712 authored Nov 8, 2024
1 parent dfae4af commit cfcd0b3
Show file tree
Hide file tree
Showing 15 changed files with 703 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.EncryptToken(byte[] innerTokenUtf8Bytes, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, System.Collections.Generic.IDictionary<string, object> additionalHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> string
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJweHeader(Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, string compressionAlgorithm, string tokenType, System.Collections.Generic.IDictionary<string, object> jweHeaderClaims, bool includeKeyIdInHeader) -> byte[]
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJwsHeader(ref System.Text.Json.Utf8JsonWriter writer, Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary<string, object> jweHeaderClaims, System.Collections.Generic.IDictionary<string, object> jwsHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> void
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.WriteJwsHeader(ref System.Text.Json.Utf8JsonWriter writer, Microsoft.IdentityModel.Tokens.SigningCredentials signingCredentials, Microsoft.IdentityModel.Tokens.EncryptingCredentials encryptingCredentials, System.Collections.Generic.IDictionary<string, object> jweHeaderClaims, System.Collections.Generic.IDictionary<string, object> jwsHeaderClaims, string tokenType, bool includeKeyIdInHeader) -> void
static Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.StackFrames.IssuerValidatorThrew -> System.Diagnostics.StackFrame
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration,
if (result.IsValid)
return result;

StackFrame tokenValidationStackFrame = StackFrames.TokenValidationFailedNullConfigurationManager ??= new StackFrame(true);
return result.UnwrapError().AddStackFrame(tokenValidationStackFrame);
return result.UnwrapError().AddStackFrame(ValidationError.GetCurrentStackFrame());
}

if (result.IsValid)
Expand Down Expand Up @@ -277,14 +276,29 @@ private async ValueTask<ValidationResult<ValidatedToken>> ValidateJWSAsync(
return audienceValidationResult.UnwrapError().AddStackFrame(audienceValidationFailureStackFrame);
}

ValidationResult<ValidatedIssuer> issuerValidationResult = await validationParameters.IssuerValidatorAsync(
jsonWebToken.Issuer, jsonWebToken, validationParameters, callContext, cancellationToken)
.ConfigureAwait(false);
ValidationResult<ValidatedIssuer> issuerValidationResult;
try
{
issuerValidationResult = await validationParameters.IssuerValidatorAsync(
jsonWebToken.Issuer, jsonWebToken, validationParameters, callContext, cancellationToken)
.ConfigureAwait(false);

if (!issuerValidationResult.IsValid)
if (!issuerValidationResult.IsValid)
{
return issuerValidationResult.UnwrapError().AddCurrentStackFrame();
}
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
StackFrame issuerValidationFailureStackFrame = StackFrames.IssuerValidationFailed ??= new StackFrame(true);
return issuerValidationResult.UnwrapError().AddStackFrame(issuerValidationFailureStackFrame);
return new IssuerValidationError(
new MessageDetail(TokenLogMessages.IDX10269),
ValidationFailureType.IssuerValidatorThrew,
typeof(SecurityTokenInvalidIssuerException),
ValidationError.GetCurrentStackFrame(),
jsonWebToken.Issuer,
ex);
}

ValidationResult<DateTime?> replayValidationResult = validationParameters.TokenReplayValidator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ internal static class StackFrames
internal static StackFrame? TokenNull;
internal static StackFrame? TokenValidationParametersNull;
internal static StackFrame? TokenNotJWT;
internal static StackFrame? TokenValidationFailedNullConfigurationManager;
internal static StackFrame? TokenValidationFailed;
// ValidateJWEAsync
internal static StackFrame? DecryptionFailed;
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10002 = "IDX10002: Unknown exception type returned. Type: '{0}'. Message: '{1}'." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10268 = "IDX10268: Unable to validate audience, validationParameters.ValidAudiences.Count == 0." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10269 = "IDX10269: IssuerValidationDelegate threw an exception, see inner exception." -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.AlgorithmValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidAlgorithm) -> void
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.InvalidAlgorithm.get -> string
Microsoft.IdentityModel.Tokens.AlgorithmValidationError._invalidAlgorithm -> string
Microsoft.IdentityModel.Tokens.AudienceValidationError.AudienceValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.Collections.Generic.IList<string> tokenAudiences, System.Collections.Generic.IList<string> validAudiences) -> void
Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.get -> System.Collections.Generic.IList<string>
Microsoft.IdentityModel.Tokens.IssuerValidationError.InvalidIssuer.get -> string
Microsoft.IdentityModel.Tokens.IssuerValidationError.IssuerValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidIssuer, System.Exception innerException) -> void
Microsoft.IdentityModel.Tokens.IssuerValidationError.IssuerValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidIssuer, System.Exception innerException) -> void
Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedConfiguration = 1 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource
Microsoft.IdentityModel.Tokens.IssuerValidationSource.IssuerMatchedValidationParameters = 2 -> Microsoft.IdentityModel.Tokens.IssuerValidationSource
Microsoft.IdentityModel.Tokens.LifetimeValidationError._expires -> System.DateTime
Expand All @@ -31,6 +35,7 @@ static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidationParamete
static Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidationParametersNull -> System.Diagnostics.StackFrame
static Microsoft.IdentityModel.Tokens.Utility.SerializeAsSingleCommaDelimitedString(System.Collections.Generic.IList<string> strings) -> string
static Microsoft.IdentityModel.Tokens.ValidationError.GetCurrentStackFrame(string filePath = "", int lineNumber = 0, int skipFrames = 1) -> System.Diagnostics.StackFrame
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.IssuerValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.NoTokenAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.NoValidationParameterAudiencesProvided -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.SignatureAlgorithmValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.IdentityModel.Tokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ internal static class LogMessages
//public const string IDX10266 = "IDX10266: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace, validationParameters.ValidIssuers is null or empty and ConfigurationManager is null.";
public const string IDX10267 = "IDX10267: '{0}' has been called by a derived class '{1}' which has not implemented this method. For this call graph to succeed, '{1}' will need to implement '{0}'.";
public const string IDX10268 = "IDX10268: Unable to validate audience, validationParameters.ValidAudiences.Count == 0.";
public const string IDX10269 = "IDX10269: IssuerValidationDelegate threw an exception, see inner exception.";


// 10500 - SignatureValidation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,36 @@ namespace Microsoft.IdentityModel.Tokens
{
internal class IssuerValidationError : ValidationError
{
private string? _invalidIssuer;

internal IssuerValidationError(
MessageDetail messageDetail,
Type exceptionType,
StackFrame stackFrame,
string? invalidIssuer)
: base(messageDetail, ValidationFailureType.IssuerValidationFailed, exceptionType, stackFrame)
: this(messageDetail, ValidationFailureType.IssuerValidationFailed, exceptionType, stackFrame, invalidIssuer, null)
{
_invalidIssuer = invalidIssuer;
}

internal IssuerValidationError(
MessageDetail messageDetail,
ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
string? invalidIssuer,
Exception? innerException)
: base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
InvalidIssuer = invalidIssuer;
}

internal string? InvalidIssuer { get; }

internal override Exception GetException()
{
if (ExceptionType == typeof(SecurityTokenInvalidIssuerException))
{
SecurityTokenInvalidIssuerException exception = new(MessageDetail.Message, InnerException)
{
InvalidIssuer = _invalidIssuer
InvalidIssuer = InvalidIssuer
};

return exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,11 @@ private class InvalidSecurityTokenFailure : ValidationFailureType { internal Inv
/// </summary>
public static readonly ValidationFailureType XmlValidationFailed = new XmlValidationFailure("XmlValidationFailed");
private class XmlValidationFailure : ValidationFailureType { internal XmlValidationFailure(string name) : base(name) { } }

/// <summary>
/// Defines a type that represents that a token is invalid.
/// </summary>
public static readonly ValidationFailureType IssuerValidatorThrew = new IssuerValidatorFailure("IssuerValidatorThrew");
private class IssuerValidatorFailure : ValidationFailureType { internal IssuerValidatorFailure(string name) : base(name) { } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ public IssuerSigningKeyValidationDelegate IssuerSigningKeyValidator
/// Allows overriding the delegate that will be used to validate the issuer of the token.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when the value is set as null.</exception>
/// <returns>The <see cref="IssuerValidationDelegateAsync"/> used to validate the issuer of a token</returns>
/// <returns>The <see cref="Tokens.IssuerValidationDelegateAsync"/> used to validate the issuer of a token</returns>
public IssuerValidationDelegateAsync IssuerValidatorAsync
{
get { return _issuerValidatorAsync; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ public static partial class Validators
/// <returns>An <see cref="ValidationResult{TResult}"/> that contains either the issuer that was validated or an error.</returns>
/// <remarks>An EXACT match is required.</remarks>
internal static async Task<ValidationResult<ValidatedIssuer>> ValidateIssuerAsync(
string issuer,
SecurityToken securityToken,
string? issuer,
SecurityToken? securityToken,
ValidationParameters validationParameters,
#pragma warning disable CA1801 // Review unused parameters
CallContext? callContext,
Expand Down Expand Up @@ -103,7 +103,7 @@ internal static async Task<ValidationResult<ValidatedIssuer>> ValidateIssuerAsyn
// LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer), callContext);


return new ValidatedIssuer(issuer, IssuerValidationSource.IssuerMatchedConfiguration);
return new ValidatedIssuer(issuer!, IssuerValidationSource.IssuerMatchedConfiguration);
}
}

Expand All @@ -126,7 +126,7 @@ internal static async Task<ValidationResult<ValidatedIssuer>> ValidateIssuerAsyn
//if (LogHelper.IsEnabled(EventLogLevel.Informational))
// LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer));

return new ValidatedIssuer(issuer, IssuerValidationSource.IssuerMatchedValidationParameters);
return new ValidatedIssuer(issuer!, IssuerValidationSource.IssuerMatchedValidationParameters);
}
}
}
Expand Down
Loading

0 comments on commit cfcd0b3

Please sign in to comment.