|
15 | 15 | namespace Microsoft.IdentityModel.Protocols.OpenIdConnect |
16 | 16 | { |
17 | 17 | /// <summary> |
18 | | - /// Delegate for validating additional claims in 'id_token' |
| 18 | + /// Delegate for validating additional claims in 'id_token' |
19 | 19 | /// </summary> |
20 | 20 | /// <param name="idToken"><see cref="JwtSecurityToken"/> to validate</param> |
21 | 21 | /// <param name="context"><see cref="OpenIdConnectProtocolValidationContext"/> used for validation</param> |
@@ -88,12 +88,30 @@ public virtual string GenerateNonce() |
88 | 88 | string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString() + Guid.NewGuid().ToString())); |
89 | 89 | if (RequireTimeStampInNonce) |
90 | 90 | { |
91 | | - return DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture) + "." + nonce; |
| 91 | + DateTime utcNow = |
| 92 | +#if SUPPORTS_TIME_PROVIDER |
| 93 | + TimeProvider?.GetUtcNow().UtcDateTime ?? |
| 94 | +#endif |
| 95 | + DateTime.UtcNow; |
| 96 | + |
| 97 | + return utcNow.Ticks.ToString(CultureInfo.InvariantCulture) + "." + nonce; |
92 | 98 | } |
93 | 99 |
|
94 | 100 | return nonce; |
95 | 101 | } |
96 | 102 |
|
| 103 | +#if SUPPORTS_TIME_PROVIDER |
| 104 | +#nullable enable |
| 105 | + /// <summary> |
| 106 | + /// Gets or sets the time provider. |
| 107 | + /// </summary> |
| 108 | + /// <remarks> |
| 109 | + /// If not set, fall back to using the <see cref="DateTime"/> class to obtain the current time. |
| 110 | + /// </remarks> |
| 111 | + public TimeProvider? TimeProvider { get; set; } |
| 112 | +#nullable restore |
| 113 | +#endif |
| 114 | + |
97 | 115 | /// <summary> |
98 | 116 | /// Gets the algorithm mapping between OpenIdConnect and .Net for Hash algorithms. |
99 | 117 | /// a <see cref="IDictionary{TKey, TValue}"/> that contains mappings from the JWT namespace https://datatracker.ietf.org/doc/html/rfc7518 to .Net. |
@@ -210,7 +228,7 @@ public virtual void ValidateAuthenticationResponse(OpenIdConnectProtocolValidati |
210 | 228 | if (validationContext == null) |
211 | 229 | throw LogHelper.LogArgumentNullException("validationContext"); |
212 | 230 |
|
213 | | - // no 'response' is received or 'id_token' in the response is null |
| 231 | + // no 'response' is received or 'id_token' in the response is null |
214 | 232 | if (validationContext.ProtocolMessage == null) |
215 | 233 | throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333)); |
216 | 234 |
|
@@ -254,7 +272,7 @@ public virtual void ValidateTokenResponse(OpenIdConnectProtocolValidationContext |
254 | 272 | if (validationContext == null) |
255 | 273 | throw LogHelper.LogArgumentNullException(nameof(validationContext)); |
256 | 274 |
|
257 | | - // no 'response' is recieved |
| 275 | + // no 'response' is recieved |
258 | 276 | if (validationContext.ProtocolMessage == null) |
259 | 277 | throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333)); |
260 | 278 |
|
@@ -490,8 +508,8 @@ private static void CheckHash(HashAlgorithm hashAlgorithm, string expectedValue, |
490 | 508 | /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception> |
491 | 509 | /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception> |
492 | 510 | /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and there is no 'c_hash' claim in the 'id_token'.</exception> |
493 | | - /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and the 'c_hash' claim is not a string in the 'id_token'.</exception> |
494 | | - /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the 'c_hash' claim in the 'id_token' does not correspond to the 'code' in the <see cref="OpenIdConnectMessage"/> response.</exception> |
| 511 | + /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and the 'c_hash' claim is not a string in the 'id_token'.</exception> |
| 512 | + /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the 'c_hash' claim in the 'id_token' does not correspond to the 'code' in the <see cref="OpenIdConnectMessage"/> response.</exception> |
495 | 513 | protected virtual void ValidateCHash(OpenIdConnectProtocolValidationContext validationContext) |
496 | 514 | { |
497 | 515 | LogHelper.LogVerbose(LogMessages.IDX21304); |
@@ -544,8 +562,8 @@ protected virtual void ValidateCHash(OpenIdConnectProtocolValidationContext vali |
544 | 562 | /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception> |
545 | 563 | /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception> |
546 | 564 | /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and there is no 'at_hash' claim in the id_token.</exception> |
547 | | - /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and the 'at_hash' claim is not a string in the 'id_token'.</exception> |
548 | | - /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the 'at_hash' claim in the 'id_token' does not correspond to the 'access_token' in the <see cref="OpenIdConnectMessage"/> response.</exception> |
| 565 | + /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and the 'at_hash' claim is not a string in the 'id_token'.</exception> |
| 566 | + /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the 'at_hash' claim in the 'id_token' does not correspond to the 'access_token' in the <see cref="OpenIdConnectMessage"/> response.</exception> |
549 | 567 | protected virtual void ValidateAtHash(OpenIdConnectProtocolValidationContext validationContext) |
550 | 568 | { |
551 | 569 | LogHelper.LogVerbose(LogMessages.IDX21309); |
@@ -658,7 +676,12 @@ protected virtual void ValidateNonce(OpenIdConnectProtocolValidationContext vali |
658 | 676 | throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21327, LogHelper.MarkAsNonPII(timestamp), LogHelper.MarkAsNonPII(DateTime.MinValue.Ticks.ToString(CultureInfo.InvariantCulture)), LogHelper.MarkAsNonPII(DateTime.MaxValue.Ticks.ToString(CultureInfo.InvariantCulture))), ex)); |
659 | 677 | } |
660 | 678 |
|
661 | | - DateTime utcNow = DateTime.UtcNow; |
| 679 | + DateTime utcNow = |
| 680 | +#if SUPPORTS_TIME_PROVIDER |
| 681 | + TimeProvider?.GetUtcNow().UtcDateTime ?? |
| 682 | +#endif |
| 683 | + DateTime.UtcNow; |
| 684 | + |
662 | 685 | if (nonceTime + NonceLifetime < utcNow) |
663 | 686 | throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21324, nonceFoundInJwt, LogHelper.MarkAsNonPII(nonceTime.ToString(CultureInfo.InvariantCulture)), LogHelper.MarkAsNonPII(utcNow.ToString(CultureInfo.InvariantCulture)), LogHelper.MarkAsNonPII(NonceLifetime.ToString("c", CultureInfo.InvariantCulture))))); |
664 | 687 | } |
|
0 commit comments