Skip to content

Commit 56752fd

Browse files
committed
Use TimeProvider
1 parent a9366af commit 56752fd

File tree

13 files changed

+292
-112
lines changed

13 files changed

+292
-112
lines changed

src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.CreateToken.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
namespace Microsoft.IdentityModel.JsonWebTokens
2020
{
2121
/// <summary>
22-
/// A <see cref="SecurityTokenHandler"/> designed for creating and validating Json Web Tokens.
22+
/// A <see cref="SecurityTokenHandler"/> designed for creating and validating Json Web Tokens.
2323
/// See: https://datatracker.ietf.org/doc/html/rfc7519 and http://www.rfc-editor.org/info/rfc7515.
2424
/// </summary>
2525
/// <remarks>This partial class is focused on TokenCreation.</remarks>
@@ -487,7 +487,7 @@ public virtual string CreateToken(
487487
/// <param name="payload">A string containing JSON which represents the JWT token payload.</param>
488488
/// <param name="signingCredentials">Defines the security key and algorithm that will be used to sign the JWT.</param>
489489
/// <param name="encryptingCredentials">Defines the security key and algorithm that will be used to encrypt the JWT.</param>
490-
/// <param name="compressionAlgorithm">Defines the compression algorithm that will be used to compress the JWT token payload.</param>
490+
/// <param name="compressionAlgorithm">Defines the compression algorithm that will be used to compress the JWT token payload.</param>
491491
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
492492
/// <param name="additionalInnerHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the inner JWT token header.</param>
493493
/// <exception cref="ArgumentNullException">if <paramref name="payload"/> is null.</exception>
@@ -534,7 +534,7 @@ public virtual string CreateToken(
534534
/// <param name="payload">A string containing JSON which represents the JWT token payload.</param>
535535
/// <param name="signingCredentials">Defines the security key and algorithm that will be used to sign the JWT.</param>
536536
/// <param name="encryptingCredentials">Defines the security key and algorithm that will be used to encrypt the JWT.</param>
537-
/// <param name="compressionAlgorithm">Defines the compression algorithm that will be used to compress the JWT token payload.</param>
537+
/// <param name="compressionAlgorithm">Defines the compression algorithm that will be used to compress the JWT token payload.</param>
538538
/// <param name="additionalHeaderClaims">Defines the dictionary containing any custom header claims that need to be added to the outer JWT token header.</param>
539539
/// <exception cref="ArgumentNullException">if <paramref name="payload"/> is null.</exception>
540540
/// <exception cref="ArgumentNullException">if <paramref name="signingCredentials"/> is null.</exception>
@@ -828,7 +828,11 @@ internal static void WriteJwsPayload(
828828
// By default we set these three properties only if they haven't been detected before.
829829
if (setDefaultTimesOnTokenCreation && !(expSet && iatSet && nbfSet))
830830
{
831-
DateTime now = DateTime.UtcNow;
831+
DateTime now =
832+
#if SUPPORTS_TIME_PROVIDER
833+
tokenDescriptor.TimeProvider?.GetUtcNow().UtcDateTime ??
834+
#endif
835+
DateTime.UtcNow;
832836

833837
if (!expSet)
834838
{
@@ -1302,14 +1306,14 @@ internal IEnumerable<SecurityKey> GetContentEncryptionKeys(JsonWebToken jwtToken
13021306
{
13031307
if (LogHelper.IsEnabled(EventLogLevel.Informational))
13041308
LogHelper.LogInformation(TokenLogMessages.IDX10904, key);
1305-
}
1309+
}
13061310
else if (configuration != null)
13071311
{
13081312
key = ResolveTokenDecryptionKeyFromConfig(jwtToken, configuration);
13091313
if (key != null && LogHelper.IsEnabled(EventLogLevel.Informational))
13101314
LogHelper.LogInformation(TokenLogMessages.IDX10905, key);
13111315
}
1312-
1316+
13131317
if (key != null)
13141318
keys = [key];
13151319
}

src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectProtocolValidator.cs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
namespace Microsoft.IdentityModel.Protocols.OpenIdConnect
1616
{
1717
/// <summary>
18-
/// Delegate for validating additional claims in 'id_token'
18+
/// Delegate for validating additional claims in 'id_token'
1919
/// </summary>
2020
/// <param name="idToken"><see cref="JwtSecurityToken"/> to validate</param>
2121
/// <param name="context"><see cref="OpenIdConnectProtocolValidationContext"/> used for validation</param>
@@ -88,12 +88,30 @@ public virtual string GenerateNonce()
8888
string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString() + Guid.NewGuid().ToString()));
8989
if (RequireTimeStampInNonce)
9090
{
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;
9298
}
9399

94100
return nonce;
95101
}
96102

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+
97115
/// <summary>
98116
/// Gets the algorithm mapping between OpenIdConnect and .Net for Hash algorithms.
99117
/// 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
210228
if (validationContext == null)
211229
throw LogHelper.LogArgumentNullException("validationContext");
212230

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
214232
if (validationContext.ProtocolMessage == null)
215233
throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333));
216234

@@ -254,7 +272,7 @@ public virtual void ValidateTokenResponse(OpenIdConnectProtocolValidationContext
254272
if (validationContext == null)
255273
throw LogHelper.LogArgumentNullException(nameof(validationContext));
256274

257-
// no 'response' is recieved
275+
// no 'response' is recieved
258276
if (validationContext.ProtocolMessage == null)
259277
throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333));
260278

@@ -490,8 +508,8 @@ private static void CheckHash(HashAlgorithm hashAlgorithm, string expectedValue,
490508
/// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
491509
/// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
492510
/// <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>
495513
protected virtual void ValidateCHash(OpenIdConnectProtocolValidationContext validationContext)
496514
{
497515
LogHelper.LogVerbose(LogMessages.IDX21304);
@@ -544,8 +562,8 @@ protected virtual void ValidateCHash(OpenIdConnectProtocolValidationContext vali
544562
/// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
545563
/// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
546564
/// <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>
549567
protected virtual void ValidateAtHash(OpenIdConnectProtocolValidationContext validationContext)
550568
{
551569
LogHelper.LogVerbose(LogMessages.IDX21309);
@@ -658,7 +676,12 @@ protected virtual void ValidateNonce(OpenIdConnectProtocolValidationContext vali
658676
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));
659677
}
660678

661-
DateTime utcNow = DateTime.UtcNow;
679+
DateTime utcNow =
680+
#if SUPPORTS_TIME_PROVIDER
681+
TimeProvider?.GetUtcNow().UtcDateTime ??
682+
#endif
683+
DateTime.UtcNow;
684+
662685
if (nonceTime + NonceLifetime < utcNow)
663686
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)))));
664687
}

src/Microsoft.IdentityModel.Protocols.SignedHttpRequest/SignedHttpRequestCreationParameters.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class SignedHttpRequestCreationParameters
1414
/// Gets or sets a value indicating whether the <see cref="ConfirmationClaimTypes.Cnf"/> claim should be created and added or not.
1515
/// </summary>
1616
/// <remarks>
17-
/// <see cref="SignedHttpRequestDescriptor.CnfClaimValue"/> will be used as a "cnf" claim value, if set.
17+
/// <see cref="SignedHttpRequestDescriptor.CnfClaimValue"/> will be used as a "cnf" claim value, if set.
1818
/// Otherwise, a "cnf" claim value will be derived from <see cref="SignedHttpRequestDescriptor.SigningCredentials"/>.
1919
/// </remarks>
2020
public bool CreateCnf { get; set; } = true;
@@ -27,19 +27,19 @@ public class SignedHttpRequestCreationParameters
2727
/// <summary>
2828
/// Gets or sets a value indicating whether the <see cref="SignedHttpRequestClaimTypes.Ts"/> claim should be created and added or not.
2929
/// </summary>
30-
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
30+
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
3131
public bool CreateTs { get; set; } = true;
3232

3333
/// <summary>
3434
/// Gets or sets a value indicating whether the <see cref="SignedHttpRequestClaimTypes.M"/> claim should be created and added or not.
3535
/// </summary>
36-
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
36+
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
3737
public bool CreateM { get; set; } = true;
3838

3939
/// <summary>
4040
/// Gets or sets a value indicating whether the <see cref="SignedHttpRequestClaimTypes.U"/> claim should be created and added or not.
4141
/// </summary>
42-
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
42+
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
4343
public bool CreateU { get; set; } = true;
4444

4545
/// <summary>
@@ -51,19 +51,19 @@ public class SignedHttpRequestCreationParameters
5151
/// <summary>
5252
/// Gets or sets a value indicating whether the <see cref="SignedHttpRequestClaimTypes.Q"/> claim should be created and added or not.
5353
/// </summary>
54-
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
54+
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
5555
public bool CreateQ { get; set; } = false;
5656

5757
/// <summary>
5858
/// Gets or sets a value indicating whether the <see cref="SignedHttpRequestClaimTypes.H"/> claim should be created and added or not.
5959
/// </summary>
60-
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
60+
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
6161
public bool CreateH { get; set; } = false;
6262

6363
/// <summary>
6464
/// Gets or sets a value indicating whether the <see cref="SignedHttpRequestClaimTypes.B"/> claim should be created and added or not.
6565
/// </summary>
66-
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
66+
/// <remarks>https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03#section-3</remarks>
6767
public bool CreateB { get; set; } = false;
6868

6969
/// <summary>
@@ -76,5 +76,17 @@ public class SignedHttpRequestCreationParameters
7676
/// </summary>
7777
/// <remarks>Allows for adjusting the local time so it matches a server time.</remarks>
7878
public TimeSpan TimeAdjustment { get; set; } = DefaultTimeAdjustment;
79+
80+
#if SUPPORTS_TIME_PROVIDER
81+
#nullable enable
82+
/// <summary>
83+
/// Gets or sets the time provider.
84+
/// </summary>
85+
/// <remarks>
86+
/// If not set, fall back to using the <see cref="DateTime"/> class to obtain the current time.
87+
/// </remarks>
88+
public TimeProvider? TimeProvider { get; set; }
89+
#nullable restore
90+
#endif
7991
}
8092
}

0 commit comments

Comments
 (0)