Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,27 @@ private async ValueTask<ValidationResult<ValidatedToken>> ValidateJWSAsync(
ex);
}

ValidationResult<DateTime?> replayValidationResult = validationParameters.TokenReplayValidator(
expires, jsonWebToken.EncodedToken, validationParameters, callContext);
ValidationResult<DateTime?> replayValidationResult;

if (!replayValidationResult.IsValid)
try
{
StackFrame replayValidationFailureStackFrame = StackFrames.ReplayValidationFailed ??= new StackFrame(true);
return replayValidationResult.UnwrapError().AddStackFrame(replayValidationFailureStackFrame);
replayValidationResult = validationParameters.TokenReplayValidator(
expires, jsonWebToken.EncodedToken, validationParameters, callContext);

if (!replayValidationResult.IsValid)
return replayValidationResult.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
{
return new TokenReplayValidationError(
new MessageDetail(TokenLogMessages.IDX10276),
ValidationFailureType.TokenReplayValidatorThrew,
typeof(SecurityTokenReplayDetectedException),
ValidationError.GetCurrentStackFrame(),
expires,
ex);
}

ValidationResult<ValidatedToken>? actorValidationResult = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,31 @@ internal async Task<ValidationResult<ValidatedToken>> ValidateTokenAsync(

if (samlToken.Assertion.Conditions is not null)
{
ValidationResult<DateTime?> tokenReplayValidationResult = Validators.ValidateTokenReplay(
samlToken.Assertion.Conditions.NotOnOrAfter,
samlToken.Assertion.CanonicalString,
validationParameters,
callContext);

if (!tokenReplayValidationResult.IsValid)
return tokenReplayValidationResult.UnwrapError().AddCurrentStackFrame();
ValidationResult<DateTime?> tokenReplayValidationResult;

try
{
tokenReplayValidationResult = validationParameters.TokenReplayValidator(
Comment thread
brentschmaltz marked this conversation as resolved.
samlToken.Assertion.Conditions.NotOnOrAfter,
samlToken.Assertion.CanonicalString,
validationParameters,
callContext);

if (!tokenReplayValidationResult.IsValid)
return tokenReplayValidationResult.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
{
return new TokenReplayValidationError(
new MessageDetail(Tokens.LogMessages.IDX10276),
ValidationFailureType.TokenReplayValidatorThrew,
typeof(SecurityTokenReplayDetectedException),
ValidationError.GetCurrentStackFrame(),
samlToken.Assertion.Conditions.NotOnOrAfter,
ex);
}
}

ValidationResult<SecurityKey> signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,31 @@ internal async Task<ValidationResult<ValidatedToken>> ValidateTokenAsync(

if (samlToken.Assertion.Conditions is not null)
{
ValidationResult<DateTime?> tokenReplayValidationResult = Validators.ValidateTokenReplay(
samlToken.Assertion.Conditions.NotOnOrAfter,
samlToken.Assertion.CanonicalString,
validationParameters,
callContext);
ValidationResult<DateTime?> tokenReplayValidationResult;

if (!tokenReplayValidationResult.IsValid)
return tokenReplayValidationResult.UnwrapError().AddCurrentStackFrame();
try
{
tokenReplayValidationResult = validationParameters.TokenReplayValidator(
samlToken.Assertion.Conditions.NotOnOrAfter,
samlToken.Assertion.CanonicalString,
validationParameters,
callContext);

if (!tokenReplayValidationResult.IsValid)
return tokenReplayValidationResult.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
{
return new TokenReplayValidationError(
new MessageDetail(Tokens.LogMessages.IDX10276),
ValidationFailureType.TokenReplayValidatorThrew,
typeof(SecurityTokenReplayDetectedException),
ValidationError.GetCurrentStackFrame(),
samlToken.Assertion.Conditions.NotOnOrAfter,
ex);
}
}

var signatureValidationResult = ValidateSignature(samlToken, validationParameters, callContext);
Expand Down
16 changes: 7 additions & 9 deletions src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const Microsoft.IdentityModel.Tokens.LogMessages.IDX10002 = "IDX10002: Unknown e
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
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10271 = "IDX10271: LifetimeValidationDelegate threw an exception, see inner exception." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10275 = "IDX10275: TokenTypeValidationDelegate threw an exception, see inner exception." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10276 = "IDX10276: TokenReplayValidationDelegate threw an exception, see inner exception." -> string
Comment thread
brentschmaltz marked this conversation as resolved.
Microsoft.IdentityModel.Tokens.AlgorithmValidationError
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.AlgorithmValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidAlgorithm, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.AlgorithmValidationError.InvalidAlgorithm.get -> string
Expand All @@ -12,17 +12,16 @@ Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.get -> Sys
Microsoft.IdentityModel.Tokens.AudienceValidationError.TokenAudiences.set -> void
Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.get -> System.Collections.Generic.IList<string>
Microsoft.IdentityModel.Tokens.AudienceValidationError.ValidAudiences.set -> void
Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError
Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.InvalidSigningKey.get -> Microsoft.IdentityModel.Tokens.SecurityKey
Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.InvalidSigningKey.set -> void
Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.IssuerSigningKeyValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, Microsoft.IdentityModel.Tokens.SecurityKey invalidSigningKey, Microsoft.IdentityModel.Tokens.ValidationFailureType failureType = null, System.Exception innerException = null) -> void
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 = null) -> 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.get -> System.DateTime?
Microsoft.IdentityModel.Tokens.LifetimeValidationError.LifetimeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.DateTime? notBefore, System.DateTime? expires, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.LifetimeValidationError.NotBefore.get -> System.DateTime?
Microsoft.IdentityModel.Tokens.TokenReplayValidationError
Microsoft.IdentityModel.Tokens.TokenReplayValidationError.ExpirationTime.get -> System.DateTime?
Microsoft.IdentityModel.Tokens.TokenReplayValidationError.TokenReplayValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, System.DateTime? expirationTime, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.TokenTypeValidationError
Microsoft.IdentityModel.Tokens.TokenTypeValidationError.TokenTypeValidationError(Microsoft.IdentityModel.Tokens.MessageDetail messageDetail, Microsoft.IdentityModel.Tokens.ValidationFailureType validationFailureType, System.Type exceptionType, System.Diagnostics.StackFrame stackFrame, string invalidTokenType, System.Exception innerException = null) -> void
Microsoft.IdentityModel.Tokens.TokenTypeValidationError._invalidTokenType -> string
Expand All @@ -37,17 +36,16 @@ Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.Error.get -> Microsoft.
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.IsValid.get -> bool
Microsoft.IdentityModel.Tokens.ValidationResult<TResult>.Result.get -> TResult
override Microsoft.IdentityModel.Tokens.AlgorithmValidationError.GetException() -> System.Exception
override Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.GetException() -> System.Exception
override Microsoft.IdentityModel.Tokens.TokenReplayValidationError.GetException() -> System.Exception
override Microsoft.IdentityModel.Tokens.TokenTypeValidationError.GetException() -> System.Exception
static Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.IssuerSigningKeyValidationError
static Microsoft.IdentityModel.Tokens.TokenReplayValidationError.NullParameter(string parameterName, System.Diagnostics.StackFrame stackFrame) -> Microsoft.IdentityModel.Tokens.TokenReplayValidationError
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.LifetimeValidatorThrew -> 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
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenExceedsMaximumSize -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenIsNotSigned -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenTypeValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.TokenReplayValidatorThrew -> Microsoft.IdentityModel.Tokens.ValidationFailureType
static readonly Microsoft.IdentityModel.Tokens.ValidationFailureType.XmlValidationFailed -> Microsoft.IdentityModel.Tokens.ValidationFailureType
2 changes: 1 addition & 1 deletion src/Microsoft.IdentityModel.Tokens/LogMessages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ internal static class LogMessages
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.";

public const string IDX10276 = "IDX10276: TokenReplayValidationDelegate threw an exception, see inner exception.";

// 10500 - SignatureValidation
public const string IDX10500 = "IDX10500: Signature validation failed. No security keys were provided to validate the signature.";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Diagnostics;

#nullable enable
namespace Microsoft.IdentityModel.Tokens
{
internal class TokenReplayValidationError : ValidationError
Comment thread
brentschmaltz marked this conversation as resolved.
{
internal TokenReplayValidationError(
MessageDetail messageDetail,
ValidationFailureType validationFailureType,
Type exceptionType,
StackFrame stackFrame,
DateTime? expirationTime,
Exception? innerException = null)
: base(messageDetail, validationFailureType, exceptionType, stackFrame, innerException)
{
ExpirationTime = expirationTime;
}

internal override Exception GetException()
{
if (ExceptionType == typeof(SecurityTokenReplayDetectedException))
{
SecurityTokenReplayDetectedException exception = new(MessageDetail.Message, InnerException);
exception.SetValidationError(this);

return exception;
}
else if (ExceptionType == typeof(SecurityTokenReplayAddFailedException))
{
SecurityTokenReplayAddFailedException exception = new(MessageDetail.Message, InnerException);
exception.SetValidationError(this);

return exception;
}

return base.GetException();
}

internal static new TokenReplayValidationError NullParameter(string parameterName, StackFrame stackFrame) => new(
MessageDetail.NullParameter(parameterName),
ValidationFailureType.NullArgument,
typeof(SecurityTokenArgumentNullException),
stackFrame,
null);

protected DateTime? ExpirationTime { get; }
}
}
#nullable restore
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,10 @@ private class XmlValidationFailure : ValidationFailureType { internal XmlValidat
/// </summary>
public static readonly ValidationFailureType IssuerValidatorThrew = new IssuerValidatorFailure("IssuerValidatorThrew");
private class IssuerValidatorFailure : ValidationFailureType { internal IssuerValidatorFailure(string name) : base(name) { } }

/// <summary>
/// Defines a type that represents the fact that the token replay validation delegate threw an exception.
/// </summary>
public static readonly ValidationFailureType TokenReplayValidatorThrew = new TokenReplayValidationFailure("TokenReplayValidatorThrew");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics;

namespace Microsoft.IdentityModel.Tokens
{
Expand Down Expand Up @@ -43,44 +42,47 @@ public static partial class Validators
#pragma warning restore CA1801 // Review unused parameters
{
if (string.IsNullOrWhiteSpace(securityToken))
return ValidationError.NullParameter(
return TokenReplayValidationError.NullParameter(
nameof(securityToken),
new StackFrame(true));
ValidationError.GetCurrentStackFrame());

if (validationParameters == null)
return ValidationError.NullParameter(
return TokenReplayValidationError.NullParameter(
nameof(validationParameters),
new StackFrame(true));
ValidationError.GetCurrentStackFrame());

// check if token if replay cache is set, then there must be an expiration time.
if (validationParameters.TokenReplayCache != null)
{
if (expirationTime == null)
return new ValidationError(
return new TokenReplayValidationError(
new MessageDetail(
LogMessages.IDX10227,
securityToken),
ValidationFailureType.TokenReplayValidationFailed,
typeof(SecurityTokenNoExpirationException),
new StackFrame(true));
ValidationError.GetCurrentStackFrame(),
expirationTime);

if (validationParameters.TokenReplayCache.TryFind(securityToken))
return new ValidationError(
return new TokenReplayValidationError(
new MessageDetail(
LogMessages.IDX10228,
securityToken),
ValidationFailureType.TokenReplayValidationFailed,
typeof(SecurityTokenReplayDetectedException),
new StackFrame(true));
ValidationError.GetCurrentStackFrame(),
expirationTime);

if (!validationParameters.TokenReplayCache.TryAdd(securityToken, expirationTime.Value))
return new ValidationError(
return new TokenReplayValidationError(
new MessageDetail(
LogMessages.IDX10229,
securityToken),
ValidationFailureType.TokenReplayValidationFailed,
typeof(SecurityTokenReplayAddFailedException),
new StackFrame(true));
ValidationError.GetCurrentStackFrame(),
expirationTime);
}

// if it reaches here, that means no token replay is detected.
Expand Down
Loading