Skip to content

Commit e79c59e

Browse files
authored
Move validators to partial classes (#2671)
* Split remaining validators into partial classes
1 parent 3187d34 commit e79c59e

File tree

5 files changed

+297
-262
lines changed

5 files changed

+297
-262
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Linq;
6+
using Microsoft.IdentityModel.Logging;
7+
8+
namespace Microsoft.IdentityModel.Tokens
9+
{
10+
public static partial class Validators
11+
{
12+
/// <summary>
13+
/// Validates if a given algorithm for a <see cref="SecurityKey"/> is valid.
14+
/// </summary>
15+
/// <param name="algorithm">The algorithm to be validated.</param>
16+
/// <param name="securityKey">The <see cref="SecurityKey"/> that signed the <see cref="SecurityToken"/>.</param>
17+
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
18+
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
19+
public static void ValidateAlgorithm(string algorithm, SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters)
20+
{
21+
if (validationParameters == null)
22+
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
23+
24+
if (validationParameters.AlgorithmValidator != null)
25+
{
26+
if (!validationParameters.AlgorithmValidator(algorithm, securityKey, securityToken, validationParameters))
27+
{
28+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10697, LogHelper.MarkAsNonPII(algorithm), securityKey))
29+
{
30+
InvalidAlgorithm = algorithm,
31+
});
32+
}
33+
34+
return;
35+
}
36+
37+
if (validationParameters.ValidAlgorithms != null && validationParameters.ValidAlgorithms.Any() && !validationParameters.ValidAlgorithms.Contains(algorithm, StringComparer.Ordinal))
38+
{
39+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAlgorithmException(LogHelper.FormatInvariant(LogMessages.IDX10696, LogHelper.MarkAsNonPII(algorithm)))
40+
{
41+
InvalidAlgorithm = algorithm,
42+
});
43+
}
44+
}
45+
}
46+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Security.Cryptography.X509Certificates;
6+
using Microsoft.IdentityModel.Abstractions;
7+
using Microsoft.IdentityModel.Logging;
8+
9+
namespace Microsoft.IdentityModel.Tokens
10+
{
11+
public static partial class Validators
12+
{
13+
/// <summary>
14+
/// Validates the <see cref="SecurityKey"/> that signed a <see cref="SecurityToken"/>.
15+
/// </summary>
16+
/// <param name="securityKey">The <see cref="SecurityKey"/> that signed the <see cref="SecurityToken"/>.</param>
17+
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
18+
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
19+
/// <exception cref="ArgumentNullException"> if 'securityKey' is null and ValidateIssuerSigningKey is true.</exception>
20+
/// <exception cref="ArgumentNullException"> if 'securityToken' is null and ValidateIssuerSigningKey is true.</exception>
21+
/// <exception cref="ArgumentNullException"> if 'validationParameters' is null.</exception>
22+
public static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters)
23+
{
24+
ValidateIssuerSecurityKey(securityKey, securityToken, validationParameters, null);
25+
}
26+
27+
/// <summary>
28+
/// Validates the <see cref="SecurityKey"/> that signed a <see cref="SecurityToken"/>.
29+
/// </summary>
30+
/// <param name="securityKey">The <see cref="SecurityKey"/> that signed the <see cref="SecurityToken"/>.</param>
31+
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
32+
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
33+
/// <param name="configuration">The <see cref="BaseConfiguration"/> required for issuer and signing key validation.</param>
34+
/// <exception cref="ArgumentNullException"> if 'securityKey' is null and ValidateIssuerSigningKey is true.</exception>
35+
/// <exception cref="ArgumentNullException"> if 'securityToken' is null and ValidateIssuerSigningKey is true.</exception>
36+
/// <exception cref="ArgumentNullException"> if 'validationParameters' is null.</exception>
37+
internal static void ValidateIssuerSecurityKey(SecurityKey securityKey, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)
38+
{
39+
if (validationParameters == null)
40+
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
41+
42+
if (validationParameters.IssuerSigningKeyValidatorUsingConfiguration != null)
43+
{
44+
if (!validationParameters.IssuerSigningKeyValidatorUsingConfiguration(securityKey, securityToken, validationParameters, configuration))
45+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey });
46+
47+
return;
48+
}
49+
50+
if (validationParameters.IssuerSigningKeyValidator != null)
51+
{
52+
if (!validationParameters.IssuerSigningKeyValidator(securityKey, securityToken, validationParameters))
53+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10232, securityKey)) { SigningKey = securityKey });
54+
55+
return;
56+
}
57+
58+
if (!validationParameters.ValidateIssuerSigningKey)
59+
{
60+
LogHelper.LogVerbose(LogMessages.IDX10237);
61+
return;
62+
}
63+
64+
if (!validationParameters.RequireSignedTokens && securityKey == null)
65+
{
66+
LogHelper.LogInformation(LogMessages.IDX10252);
67+
return;
68+
}
69+
else if (securityKey == null)
70+
{
71+
throw LogHelper.LogExceptionMessage(new ArgumentNullException(nameof(securityKey), LogMessages.IDX10253));
72+
}
73+
74+
if (securityToken == null)
75+
throw LogHelper.LogArgumentNullException(nameof(securityToken));
76+
77+
ValidateIssuerSigningKeyLifeTime(securityKey, validationParameters);
78+
}
79+
80+
/// <summary>
81+
/// Given a signing key, when it's derived from a certificate, validates that the certificate is already active and non-expired
82+
/// </summary>
83+
/// <param name="securityKey">The <see cref="SecurityKey"/> that signed the <see cref="SecurityToken"/>.</param>
84+
/// <param name="validationParameters">The <see cref="TokenValidationParameters"/> that are used to validate the token.</param>
85+
internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, TokenValidationParameters validationParameters)
86+
{
87+
X509SecurityKey x509SecurityKey = securityKey as X509SecurityKey;
88+
if (x509SecurityKey?.Certificate is X509Certificate2 cert)
89+
{
90+
DateTime utcNow = DateTime.UtcNow;
91+
var notBeforeUtc = cert.NotBefore.ToUniversalTime();
92+
var notAfterUtc = cert.NotAfter.ToUniversalTime();
93+
94+
if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew))
95+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10248, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow))));
96+
97+
if (LogHelper.IsEnabled(EventLogLevel.Informational))
98+
LogHelper.LogInformation(LogMessages.IDX10250, LogHelper.MarkAsNonPII(notBeforeUtc), LogHelper.MarkAsNonPII(utcNow));
99+
100+
if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate()))
101+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10249, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow))));
102+
103+
if (LogHelper.IsEnabled(EventLogLevel.Informational))
104+
LogHelper.LogInformation(LogMessages.IDX10251, LogHelper.MarkAsNonPII(notAfterUtc), LogHelper.MarkAsNonPII(utcNow));
105+
}
106+
}
107+
}
108+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Linq;
6+
using System.Security.Cryptography.X509Certificates;
7+
using Microsoft.IdentityModel.Abstractions;
8+
using Microsoft.IdentityModel.Logging;
9+
10+
namespace Microsoft.IdentityModel.Tokens
11+
{
12+
public static partial class Validators
13+
{
14+
/// <summary>
15+
/// Validates if a token has been replayed.
16+
/// </summary>
17+
/// <param name="expirationTime">When does the security token expire.</param>
18+
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
19+
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
20+
/// <exception cref="ArgumentNullException">If 'securityToken' is null or whitespace.</exception>
21+
/// <exception cref="ArgumentNullException">If 'validationParameters' is null or whitespace.</exception>
22+
/// <exception cref="SecurityTokenNoExpirationException">If <see cref="TokenValidationParameters.TokenReplayCache"/> is not null and expirationTime.HasValue is false. When a TokenReplayCache is set, tokens require an expiration time.</exception>
23+
/// <exception cref="SecurityTokenReplayDetectedException">If the 'securityToken' is found in the cache.</exception>
24+
/// <exception cref="SecurityTokenReplayAddFailedException">If the 'securityToken' could not be added to the <see cref="TokenValidationParameters.TokenReplayCache"/>.</exception>
25+
public static void ValidateTokenReplay(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters)
26+
{
27+
if (string.IsNullOrWhiteSpace(securityToken))
28+
throw LogHelper.LogArgumentNullException(nameof(securityToken));
29+
30+
if (validationParameters == null)
31+
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
32+
33+
if (validationParameters.TokenReplayValidator != null)
34+
{
35+
if (!validationParameters.TokenReplayValidator(expirationTime, securityToken, validationParameters))
36+
throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException(
37+
LogHelper.FormatInvariant(
38+
LogMessages.IDX10228,
39+
LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString()))));
40+
return;
41+
}
42+
43+
if (!validationParameters.ValidateTokenReplay)
44+
{
45+
LogHelper.LogVerbose(LogMessages.IDX10246);
46+
return;
47+
}
48+
49+
// check if token if replay cache is set, then there must be an expiration time.
50+
if (validationParameters.TokenReplayCache != null)
51+
{
52+
if (!expirationTime.HasValue)
53+
throw LogHelper.LogExceptionMessage(new SecurityTokenNoExpirationException(LogHelper.FormatInvariant(LogMessages.IDX10227, securityToken)));
54+
55+
if (validationParameters.TokenReplayCache.TryFind(securityToken))
56+
throw LogHelper.LogExceptionMessage(new SecurityTokenReplayDetectedException(LogHelper.FormatInvariant(LogMessages.IDX10228, securityToken)));
57+
58+
if (!validationParameters.TokenReplayCache.TryAdd(securityToken, expirationTime.Value))
59+
throw LogHelper.LogExceptionMessage(new SecurityTokenReplayAddFailedException(LogHelper.FormatInvariant(LogMessages.IDX10229, securityToken)));
60+
}
61+
62+
// if it reaches here, that means no token replay is detected.
63+
LogHelper.LogInformation(LogMessages.IDX10240);
64+
}
65+
66+
/// <summary>
67+
/// Validates if a token has been replayed.
68+
/// </summary>
69+
/// <param name="securityToken">The <see cref="SecurityToken"/> being validated.</param>
70+
/// <param name="expirationTime">When does the security token expire.</param>
71+
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
72+
/// <exception cref="ArgumentNullException">If 'securityToken' is null or whitespace.</exception>
73+
/// <exception cref="ArgumentNullException">If 'validationParameters' is null or whitespace.</exception>
74+
/// <exception cref="SecurityTokenNoExpirationException">If <see cref="TokenValidationParameters.TokenReplayCache"/> is not null and expirationTime.HasValue is false. When a TokenReplayCache is set, tokens require an expiration time.</exception>
75+
/// <exception cref="SecurityTokenReplayDetectedException">If the 'securityToken' is found in the cache.</exception>
76+
/// <exception cref="SecurityTokenReplayAddFailedException">If the 'securityToken' could not be added to the <see cref="TokenValidationParameters.TokenReplayCache"/>.</exception>
77+
public static void ValidateTokenReplay(string securityToken, DateTime? expirationTime, TokenValidationParameters validationParameters)
78+
{
79+
ValidateTokenReplay(expirationTime, securityToken, validationParameters);
80+
}
81+
}
82+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Linq;
6+
using Microsoft.IdentityModel.Abstractions;
7+
using Microsoft.IdentityModel.Logging;
8+
9+
namespace Microsoft.IdentityModel.Tokens
10+
{
11+
public static partial class Validators
12+
{
13+
/// <summary>
14+
/// Validates the type of the token.
15+
/// </summary>
16+
/// <param name="type">The token type or <c>null</c> if it couldn't be resolved (e.g from the 'typ' header for a JWT).</param>
17+
/// <param name="securityToken">The <see cref="SecurityToken"/> that is being validated.</param>
18+
/// <param name="validationParameters"><see cref="TokenValidationParameters"/> required for validation.</param>
19+
/// <exception cref="ArgumentNullException">If <paramref name="validationParameters"/> is null.</exception>
20+
/// <exception cref="ArgumentNullException">If <paramref name="securityToken"/> is null.</exception>
21+
/// <exception cref="SecurityTokenInvalidTypeException">If <paramref name="type"/> is null or whitespace and <see cref="TokenValidationParameters.ValidTypes"/> is not null.</exception>
22+
/// <exception cref="SecurityTokenInvalidTypeException">If <paramref name="type"/> failed to match <see cref="TokenValidationParameters.ValidTypes"/>.</exception>
23+
/// <remarks>An EXACT match is required. <see cref="StringComparison.Ordinal"/> (case sensitive) is used for comparing <paramref name="type"/> against <see cref="TokenValidationParameters.ValidTypes"/>.</remarks>
24+
/// <returns>The actual token type, that may be the same as <paramref name="type"/> or a different value if the token type was resolved from a different location.</returns>
25+
public static string ValidateTokenType(string type, SecurityToken securityToken, TokenValidationParameters validationParameters)
26+
{
27+
if (securityToken == null)
28+
throw new ArgumentNullException(nameof(securityToken));
29+
30+
if (validationParameters == null)
31+
throw LogHelper.LogArgumentNullException(nameof(validationParameters));
32+
33+
if (validationParameters.TypeValidator == null && (validationParameters.ValidTypes == null || !validationParameters.ValidTypes.Any()))
34+
{
35+
LogHelper.LogVerbose(LogMessages.IDX10255);
36+
return type;
37+
}
38+
39+
if (validationParameters.TypeValidator != null)
40+
return validationParameters.TypeValidator(type, securityToken, validationParameters);
41+
42+
// Note: don't throw an exception for a null or empty token type when a user-defined delegate is set
43+
// to allow it to extract the actual token type from a different location (e.g from the claims).
44+
if (string.IsNullOrEmpty(type))
45+
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidTypeException(LogMessages.IDX10256) { InvalidType = null });
46+
47+
if (!validationParameters.ValidTypes.Contains(type, StringComparer.Ordinal))
48+
{
49+
throw LogHelper.LogExceptionMessage(
50+
new SecurityTokenInvalidTypeException(LogHelper.FormatInvariant(LogMessages.IDX10257, LogHelper.MarkAsNonPII(type), Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidTypes)))
51+
{ InvalidType = type });
52+
}
53+
54+
// if it reaches here, token type was succcessfully validated.
55+
if (LogHelper.IsEnabled(EventLogLevel.Informational))
56+
LogHelper.LogInformation(LogMessages.IDX10258, LogHelper.MarkAsNonPII(type));
57+
58+
return type;
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)