Skip to content

Commit

Permalink
Establish base for ValidationParameters refactor (#2709)
Browse files Browse the repository at this point in the history
* Base for new ValidationParameters

* Clean up new files. Keep the original validator code in Validators.cs

---------

Co-authored-by: id4s <user@contoso.com>
  • Loading branch information
iNinja and HP712 authored Jul 15, 2024
1 parent 323dda3 commit 863ba0f
Show file tree
Hide file tree
Showing 9 changed files with 1,082 additions and 491 deletions.
503 changes: 503 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/Validation/ValidationParameters.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,5 @@ namespace Microsoft.IdentityModel.Tokens
{
public static partial class Validators
{
/// <summary>
/// Validates if a given algorithm for a <see cref="SecurityKey"/> is valid.
/// </summary>
/// <param name="algorithm">The algorithm to be validated.</param>
/// <param name="securityKey">The <see cref="SecurityKey"/> that signed the <see cref="SecurityToken"/>.</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
public static void ValidateAlgorithm(string algorithm, SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));

if (validationParameters.AlgorithmValidator != null)
{
if (!validationParameters.AlgorithmValidator(algorithm, securityKey, securityToken, validationParameters))
{
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10697, LogHelper.MarkAsNonPII(algorithm), securityKey))
{
InvalidAlgorithm = algorithm,
});
}

return;
}

if (validationParameters.ValidAlgorithms != null && validationParameters.ValidAlgorithms.Any() && !validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal))
{
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10696, LogHelper.MarkAsNonPII(algorithm)))
{
InvalidAlgorithm = algorithm,
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,74 +31,6 @@ internal delegate AudienceValidationResult ValidateAudience(
/// </summary>
public static partial class Validators
{
/// <summary>
/// Determines if the audiences found in a <see cref="SecurityToken"/> are valid.
/// </summary>
/// <param name="audiences">The audiences found in the <see cref="SecurityToken"/>.</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
/// <exception cref="ArgumentNullException">If 'validationParameters' is null.</exception>
/// <exception cref="ArgumentNullException">If 'audiences' is null and <see cref="TokenValidationParameters.ValidateAudience"/> is true.</exception>
/// <exception cref="SecurityTokenInvalidAudienceException">If <see cref="TokenValidationParameters.ValidAudience"/> is null or whitespace and <see cref="TokenValidationParameters.ValidAudiences"/> is null.</exception>
/// <exception cref="SecurityTokenInvalidAudienceException">If none of the 'audiences' matched either <see cref="TokenValidationParameters.ValidAudience"/> or one of <see cref="TokenValidationParameters.ValidAudiences"/>.</exception>
/// <remarks>An EXACT match is required.</remarks>
public static void ValidateAudience(IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));

if (validationParameters.AudienceValidator != null)
{
if (!validationParameters.AudienceValidator(audiences, securityToken, validationParameters))
throw LogHelper.LogExceptionMessage(
new SecurityTokenInvalidAudienceException(
LogHelper.FormatInvariant(
LogMessages.IDX10231,
LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString())))
{
InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences)
});

return;
}

if (!validationParameters.ValidateAudience)
{
LogHelper.LogWarning(LogMessages.IDX10233);
return;
}

if (audiences == null)
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10207) { InvalidAudience = null });

if (string.IsNullOrWhiteSpace(validationParameters.ValidAudience) && (validationParameters.ValidAudiences == null))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10208) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) });

if (!audiences.Any())
throw LogHelper.LogExceptionMessage(
new SecurityTokenInvalidAudienceException(LogHelper.FormatInvariant(LogMessages.IDX10206))
{ InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) });

if (audiences is not List<string> audiencesAsList)
audiencesAsList = audiences.ToList();

if (AudienceIsValid(audiencesAsList, validationParameters))
return;

SecurityTokenInvalidAudienceException ex = new SecurityTokenInvalidAudienceException(
LogHelper.FormatInvariant(LogMessages.IDX10214,
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(audiences)),
LogHelper.MarkAsNonPII(validationParameters.ValidAudience ?? "null"),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences))))
{ InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) };

if (!validationParameters.LogValidationExceptions)
throw ex;

throw LogHelper.LogExceptionMessage(ex);
}


/// <summary>
/// Determines if the audiences found in a <see cref="SecurityToken"/> are valid.
/// </summary>
Expand Down Expand Up @@ -259,13 +191,13 @@ private static bool AudiencesMatch(bool ignoreTrailingSlashWhenValidatingAudienc
{
if (validAudience.Length == tokenAudience.Length)
return string.Equals(validAudience, tokenAudience);
else if (ignoreTrailingSlashWhenValidatingAudience && AudiencesMatchIgnoringTrailingSlash(tokenAudience, validAudience))
else if (ignoreTrailingSlashWhenValidatingAudience && NewAudiencesMatchIgnoringTrailingSlash(tokenAudience, validAudience))
return true;

return false;
}

private static bool AudiencesMatchIgnoringTrailingSlash(string tokenAudience, string validAudience)
private static bool NewAudiencesMatchIgnoringTrailingSlash(string tokenAudience, string validAudience)
{
int length = -1;

Expand Down
141 changes: 0 additions & 141 deletions src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,147 +32,6 @@ internal delegate Task<IssuerValidationResult> IssuerValidationDelegateAsync(
/// </summary>
public static partial class Validators
{
/// <summary>
/// Determines if an issuer found in a <see cref="SecurityToken"/> is valid.
/// </summary>
/// <param name="issuer">The issuer to validate</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
/// <returns>The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity".</returns>
/// <exception cref="ArgumentNullException">If 'validationParameters' is null.</exception>
/// <exception cref="ArgumentNullException">If 'issuer' is null or whitespace and <see cref="TokenValidationParameters.ValidateIssuer"/> is true.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If <see cref="TokenValidationParameters.ValidIssuer"/> is null or whitespace and <see cref="TokenValidationParameters.ValidIssuers"/> is null.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If 'issuer' failed to matched either <see cref="TokenValidationParameters.ValidIssuer"/> or one of <see cref="TokenValidationParameters.ValidIssuers"/>.</exception>
/// <remarks>An EXACT match is required.</remarks>
public static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
return ValidateIssuer(issuer, securityToken, validationParameters, null);
}

/// <summary>
/// Determines if an issuer found in a <see cref="SecurityToken"/> is valid.
/// </summary>
/// <param name="issuer">The issuer to validate</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
/// <param name="configuration">The <see cref="BaseConfiguration"/> required for issuer and signing key validation.</param>
/// <returns>The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity".</returns>
/// <exception cref="ArgumentNullException">If 'validationParameters' is null.</exception>
/// <exception cref="ArgumentNullException">If 'issuer' is null or whitespace and <see cref="TokenValidationParameters.ValidateIssuer"/> is true.</exception>
/// <exception cref="ArgumentNullException">If ' configuration' is null.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If <see cref="TokenValidationParameters.ValidIssuer"/> is null or whitespace and <see cref="TokenValidationParameters.ValidIssuers"/> is null and <see cref="BaseConfiguration.Issuer"/> is null.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If 'issuer' failed to matched either <see cref="TokenValidationParameters.ValidIssuer"/> or one of <see cref="TokenValidationParameters.ValidIssuers"/> or <see cref="BaseConfiguration.Issuer"/>.</exception>
/// <remarks>An EXACT match is required.</remarks>
internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
{
ValueTask<string> vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration);
return vt.IsCompletedSuccessfully ?
vt.Result :
vt.AsTask().ConfigureAwait(false).GetAwaiter().GetResult();
}

/// <summary>
/// Determines if an issuer found in a <see cref="SecurityToken"/> is valid.
/// </summary>
/// <param name="issuer">The issuer to validate</param>
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> to be used for validating the token.</param>
/// <param name="configuration">The <see cref="BaseConfiguration"/> required for issuer and signing key validation.</param>
/// <returns>The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity".</returns>
/// <exception cref="ArgumentNullException">If 'validationParameters' is null.</exception>
/// <exception cref="ArgumentNullException">If 'issuer' is null or whitespace and <see cref="TokenValidationParameters.ValidateIssuer"/> is true.</exception>
/// <exception cref="ArgumentNullException">If ' configuration' is null.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If <see cref="TokenValidationParameters.ValidIssuer"/> is null or whitespace and <see cref="TokenValidationParameters.ValidIssuers"/> is null and <see cref="BaseConfiguration.Issuer"/> is null.</exception>
/// <exception cref="SecurityTokenInvalidIssuerException">If 'issuer' failed to matched either <see cref="TokenValidationParameters.ValidIssuer"/> or one of <see cref="TokenValidationParameters.ValidIssuers"/> or <see cref="BaseConfiguration.Issuer"/>.</exception>
/// <remarks>An EXACT match is required.</remarks>
internal static async ValueTask<string> ValidateIssuerAsync(
string issuer,
SecurityToken securityToken,
TokenValidationParameters validationParameters,
BaseConfiguration configuration)
{
if (validationParameters == null)
throw LogHelper.LogArgumentNullException(nameof(validationParameters));

if (validationParameters.IssuerValidatorAsync != null)
return await validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters).ConfigureAwait(false);

if (validationParameters.IssuerValidatorUsingConfiguration != null)
return validationParameters.IssuerValidatorUsingConfiguration(issuer, securityToken, validationParameters, configuration);

if (validationParameters.IssuerValidator != null)
return validationParameters.IssuerValidator(issuer, securityToken, validationParameters);

if (!validationParameters.ValidateIssuer)
{
LogHelper.LogWarning(LogMessages.IDX10235);
return issuer;
}

if (string.IsNullOrWhiteSpace(issuer))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10211)
{ InvalidIssuer = issuer });

// Throw if all possible places to validate against are null or empty
if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer)
&& validationParameters.ValidIssuers.IsNullOrEmpty()
&& string.IsNullOrWhiteSpace(configuration?.Issuer))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204)
{ InvalidIssuer = issuer });

if (configuration != null)
{
if (string.Equals(configuration.Issuer, issuer))
{
if (LogHelper.IsEnabled(EventLogLevel.Informational))
LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer));

return issuer;
}
}

if (string.Equals(validationParameters.ValidIssuer, issuer))
{
if (LogHelper.IsEnabled(EventLogLevel.Informational))
LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer));

return issuer;
}

if (validationParameters.ValidIssuers != null)
{
foreach (string str in validationParameters.ValidIssuers)
{
if (string.IsNullOrEmpty(str))
{
LogHelper.LogInformation(LogMessages.IDX10262);
continue;
}

if (string.Equals(str, issuer))
{
if (LogHelper.IsEnabled(EventLogLevel.Informational))
LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer));

return issuer;
}
}
}

SecurityTokenInvalidIssuerException ex = new SecurityTokenInvalidIssuerException(
LogHelper.FormatInvariant(LogMessages.IDX10205,
LogHelper.MarkAsNonPII(issuer),
LogHelper.MarkAsNonPII(validationParameters.ValidIssuer ?? "null"),
LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)),
LogHelper.MarkAsNonPII(configuration?.Issuer)))
{ InvalidIssuer = issuer };

if (!validationParameters.LogValidationExceptions)
throw ex;

throw LogHelper.LogExceptionMessage(ex);
}

/// <summary>
/// Determines if an issuer found in a <see cref="SecurityToken"/> is valid.
/// </summary>
Expand Down
Loading

0 comments on commit 863ba0f

Please sign in to comment.