From 304f05ac0d87dba97fcd3f1acdf2b9bbbe4e840a Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 12:42:15 +0200 Subject: [PATCH 01/30] Use TimeProvider --- .../IOptionWithTimeProvider.cs | 17 ++++++++++++++ .../TimeProviderExtensions.cs | 19 ++++++++++++++++ .../OpenIddictAuthorizationManager.cs | 2 +- .../Managers/OpenIddictTokenManager.cs | 2 +- .../OpenIddictCoreExtensions.cs | 2 ++ src/OpenIddict.Core/OpenIddictCoreOptions.cs | 9 +++++++- .../OpenIddictCoreOptionsPostConfigure.cs | 22 +++++++++++++++++++ 7 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs create mode 100644 src/OpenIddict.Abstractions/TimeProviderExtensions.cs create mode 100644 src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs diff --git a/src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs b/src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs new file mode 100644 index 000000000..2f0cc1767 --- /dev/null +++ b/src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs @@ -0,0 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +namespace OpenIddict.Abstractions; + +public interface IOptionWithTimeProvider +{ +#if SUPPORTS_TIME_PROVIDER + /// + /// Gets the time provider + /// + public TimeProvider? TimeProvider { get; } +#endif +} diff --git a/src/OpenIddict.Abstractions/TimeProviderExtensions.cs b/src/OpenIddict.Abstractions/TimeProviderExtensions.cs new file mode 100644 index 000000000..27607c24f --- /dev/null +++ b/src/OpenIddict.Abstractions/TimeProviderExtensions.cs @@ -0,0 +1,19 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +namespace OpenIddict.Abstractions; + +public static class TimeProviderExtensions +{ + public static DateTimeOffset GetUtcNow(this T option) where T : IOptionWithTimeProvider + { +#if SUPPORTS_TIME_PROVIDER + return option.TimeProvider!.GetUtcNow(); +#else + return DateTimeOffset.UtcNow; +#endif + } +} diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index b3f5cfb34..d57e8de1b 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -232,7 +232,7 @@ await foreach (var result in ValidateAsync(authorization, cancellationToken)) var descriptor = new OpenIddictAuthorizationDescriptor { ApplicationId = client, - CreationDate = DateTimeOffset.UtcNow, + CreationDate = Options.CurrentValue.GetUtcNow(), Principal = principal, Status = Statuses.Valid, Subject = subject, diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs index 26a8cb6f9..c92e77514 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs @@ -1088,7 +1088,7 @@ public virtual async ValueTask TryRedeemAsync(TToken token, CancellationTo // the first time the token is redeemed. In this case, attach the current date. if (await Store.GetRedemptionDateAsync(token, cancellationToken) is null) { - await Store.SetRedemptionDateAsync(token, DateTimeOffset.UtcNow, cancellationToken); + await Store.SetRedemptionDateAsync(token, Options.CurrentValue.GetUtcNow(), cancellationToken); } await Store.SetStatusAsync(token, Statuses.Redeemed, cancellationToken); diff --git a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs index 484e6d234..6d8c9d8a4 100644 --- a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs @@ -48,6 +48,8 @@ public static OpenIddictCoreBuilder AddCore(this OpenIddictBuilder builder) builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, OpenIddictCoreOptionsPostConfigure>()); + builder.Services.TryAddScoped(static provider => { var type = provider.GetRequiredService>() diff --git a/src/OpenIddict.Core/OpenIddictCoreOptions.cs b/src/OpenIddict.Core/OpenIddictCoreOptions.cs index 0ca197349..998b90464 100644 --- a/src/OpenIddict.Core/OpenIddictCoreOptions.cs +++ b/src/OpenIddict.Core/OpenIddictCoreOptions.cs @@ -9,7 +9,7 @@ namespace OpenIddict.Core; /// /// Provides various settings needed to configure the OpenIddict core services. /// -public sealed class OpenIddictCoreOptions +public sealed class OpenIddictCoreOptions : IOptionWithTimeProvider { /// /// Gets or sets the type corresponding to the default Application entity, @@ -59,4 +59,11 @@ public sealed class OpenIddictCoreOptions /// This property is not used when is . /// public int EntityCacheLimit { get; set; } = 250; + +#if SUPPORTS_TIME_PROVIDER + /// + /// Gets the time provider + /// + public TimeProvider? TimeProvider { get; set; } +#endif } diff --git a/src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs b/src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs new file mode 100644 index 000000000..2b44791f3 --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs @@ -0,0 +1,22 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using Microsoft.Extensions.Options; + +namespace OpenIddict.Core; + +public class OpenIddictCoreOptionsPostConfigure : IPostConfigureOptions +{ + public void PostConfigure(string? name, OpenIddictCoreOptions options) + { +#if SUPPORTS_TIME_PROVIDER + if (options.TimeProvider is null) + { + options.TimeProvider = TimeProvider.System; + } +#endif + } +} From a76a3f32bd7ffe2866f47024e5c5f6a03cb6cbde Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 13:08:31 +0200 Subject: [PATCH 02/30] Use TimeProvider.GetUtcNow --- src/OpenIddict.Quartz/OpenIddictQuartzJob.cs | 4 ++-- .../OpenIddictQuartzOptions.cs | 9 ++++++- .../OpenIddictServerHandlers.Protection.cs | 8 +++---- .../OpenIddictServerHandlers.cs | 24 +++++++++---------- .../OpenIddictServerOptions.cs | 9 ++++++- ...nIddictValidationHandlers.Introspection.cs | 2 +- ...OpenIddictValidationHandlers.Protection.cs | 2 +- .../OpenIddictValidationHandlers.cs | 2 +- .../OpenIddictValidationOptions.cs | 9 ++++++- ...enIddictServerIntegrationTests.Exchange.cs | 4 ++-- ...ictServerIntegrationTests.Introspection.cs | 2 +- ...enIddictServerIntegrationTests.Userinfo.cs | 2 +- 12 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs b/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs index 9e7a15c72..a3cde99e5 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs @@ -74,7 +74,7 @@ public async Task Execute(IJobExecutionContext context) UnscheduleFiringTrigger = true }; - var threshold = DateTimeOffset.UtcNow - _options.CurrentValue.MinimumTokenLifespan; + var threshold = _options.CurrentValue.GetUtcNow() - _options.CurrentValue.MinimumTokenLifespan; try { @@ -119,7 +119,7 @@ public async Task Execute(IJobExecutionContext context) UnscheduleFiringTrigger = true }; - var threshold = DateTimeOffset.UtcNow - _options.CurrentValue.MinimumAuthorizationLifespan; + var threshold = _options.CurrentValue.GetUtcNow() - _options.CurrentValue.MinimumAuthorizationLifespan; try { diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs b/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs index e8f0021b0..944ace034 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs @@ -9,7 +9,7 @@ namespace OpenIddict.Quartz; /// /// Provides various settings needed to configure the OpenIddict Quartz.NET integration. /// -public sealed class OpenIddictQuartzOptions +public sealed class OpenIddictQuartzOptions : IOptionWithTimeProvider { /// /// Gets or sets a boolean indicating whether authorizations pruning should be disabled. @@ -38,4 +38,11 @@ public sealed class OpenIddictQuartzOptions /// By default, this value is set to 14 days and cannot be less than 10 minutes. /// public TimeSpan MinimumTokenLifespan { get; set; } = TimeSpan.FromDays(14); + +#if SUPPORTS_TIME_PROVIDER + /// + /// Gets the time provider + /// + public TimeProvider? TimeProvider { get; set; } +#endif } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index d7f5f668f..8bb6eb480 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -908,7 +908,7 @@ public ValueTask HandleAsync(ValidateTokenContext context) Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); - if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < DateTimeOffset.UtcNow) + if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) { context.Reject( error: context.Principal.GetTokenType() switch @@ -1110,7 +1110,7 @@ async ValueTask IsReusableAsync(object token) } var date = await _tokenManager.GetRedemptionDateAsync(token); - if (date is null || DateTimeOffset.UtcNow < date + context.Options.RefreshTokenReuseLeeway) + if (date is null || context.Options.GetUtcNow() < date + context.Options.RefreshTokenReuseLeeway) { return true; } @@ -1404,7 +1404,7 @@ public ValueTask HandleAsync(GenerateTokenContext context) // entry to the token) that can be used by the resource servers to determine // whether an access token has already been used or blacklist them if necessary. // - // Note: scopes are deliberately formatted as a single space-separated + // Note: scopes are deliberately formatted as a single space-separated // string to respect the usual representation of the standard scope claim. // // See https://datatracker.ietf.org/doc/html/rfc9068 for more information. @@ -1421,7 +1421,7 @@ public ValueTask HandleAsync(GenerateTokenContext context) // For authorization/device/user codes and refresh tokens, // attach claims destinations to the JWT claims collection. - if (context.TokenType is TokenTypeHints.AuthorizationCode or TokenTypeHints.DeviceCode or + if (context.TokenType is TokenTypeHints.AuthorizationCode or TokenTypeHints.DeviceCode or TokenTypeHints.RefreshToken or TokenTypeHints.UserCode) { var destinations = principal.GetDestinations(); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 9df9fd883..27a3b261d 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -104,7 +104,7 @@ public static partial class OpenIddictServerHandlers */ ValidateSignOutDemand.Descriptor, AttachCustomSignOutParameters.Descriptor, - + /* * Error processing: */ @@ -2747,7 +2747,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) var descriptor = new OpenIddictAuthorizationDescriptor { - CreationDate = DateTimeOffset.UtcNow, + CreationDate = context.Options.GetUtcNow(), Principal = context.Principal, Status = Statuses.Valid, Subject = context.Principal.GetClaim(Claims.Subject), @@ -2887,7 +2887,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) claim.Properties.Remove(Properties.Destinations); } - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetAccessTokenLifetime(); @@ -3011,7 +3011,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetAuthorizationCodeLifetime(); @@ -3137,7 +3137,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetDeviceCodeLifetime(); @@ -3250,7 +3250,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed // and must exactly match the expiration date of the refresh token used in the token request. @@ -3401,7 +3401,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) claim.Properties.Remove(Properties.Destinations); } - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetIdentityTokenLifetime(); @@ -3526,7 +3526,7 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetUserCodeLifetime(); @@ -4288,9 +4288,9 @@ public ValueTask HandleAsync(ProcessSignInContext context) { // If an expiration date was set on the access token principal, return it to the client application. if (context.AccessTokenPrincipal.GetExpirationDate() - is DateTimeOffset date && date > DateTimeOffset.UtcNow) + is DateTimeOffset date && date > context.Options.GetUtcNow()) { - context.Response.ExpiresIn = (long) ((date - DateTimeOffset.UtcNow).TotalSeconds + .5); + context.Response.ExpiresIn = (long) ((date - context.Options.GetUtcNow()).TotalSeconds + .5); } // If the granted access token scopes differ from the requested scopes, return the granted scopes @@ -4352,8 +4352,8 @@ public ValueTask HandleAsync(ProcessSignInContext context) { // If an expiration date was set on the device code or user // code principal, return it to the client application. - DateTimeOffset date when date > DateTimeOffset.UtcNow - => (long) ((date - DateTimeOffset.UtcNow).TotalSeconds + .5), + DateTimeOffset date when date > context.Options.GetUtcNow() + => (long) ((date - context.Options.GetUtcNow()).TotalSeconds + .5), // Otherwise, return an arbitrary value, as the "expires_in" // parameter is required in device authorization responses. diff --git a/src/OpenIddict.Server/OpenIddictServerOptions.cs b/src/OpenIddict.Server/OpenIddictServerOptions.cs index ae2a22614..5e396b245 100644 --- a/src/OpenIddict.Server/OpenIddictServerOptions.cs +++ b/src/OpenIddict.Server/OpenIddictServerOptions.cs @@ -13,7 +13,7 @@ namespace OpenIddict.Server; /// /// Provides various settings needed to configure the OpenIddict server handler. /// -public sealed class OpenIddictServerOptions +public sealed class OpenIddictServerOptions : IOptionWithTimeProvider { /// /// Gets or sets the optional URI used to uniquely identify the authorization server. @@ -453,4 +453,11 @@ public sealed class OpenIddictServerOptions /// that provides additional protection against token leakage. /// public bool UseReferenceRefreshTokens { get; set; } + +#if SUPPORTS_TIME_PROVIDER + /// + /// Gets the time provider + /// + public TimeProvider? TimeProvider { get; set; } +#endif } diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs index 6c2b4c538..64ef8369b 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs @@ -295,7 +295,7 @@ public ValueTask HandleAsync(HandleIntrospectionResponseContext context) if (long.TryParse((string?) context.Response[Claims.ExpiresAt], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) && DateTimeOffset.FromUnixTimeSeconds(value) is DateTimeOffset date && - date.Add(context.Options.TokenValidationParameters.ClockSkew) < DateTimeOffset.UtcNow) + date.Add(context.Options.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) { context.Reject( error: Errors.InvalidToken, diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs index bd09a644d..54de6f3ad 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs @@ -646,7 +646,7 @@ public ValueTask HandleAsync(ValidateTokenContext context) Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); - if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < DateTimeOffset.UtcNow) + if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) { context.Logger.LogInformation(SR.GetResourceString(SR.ID6156)); diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs index 27123cde9..9f35b308d 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs @@ -361,7 +361,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) diff --git a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs index 5590c9483..730afe364 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs @@ -13,7 +13,7 @@ namespace OpenIddict.Validation; /// /// Provides various settings needed to configure the OpenIddict validation handler. /// -public sealed class OpenIddictValidationOptions +public sealed class OpenIddictValidationOptions : IOptionWithTimeProvider { /// /// Gets the list of encryption credentials used by the OpenIddict validation services. @@ -181,4 +181,11 @@ public sealed class OpenIddictValidationOptions ValidateAudience = false, ValidateLifetime = false }; + +#if SUPPORTS_TIME_PROVIDER + /// + /// Gets the time provider + /// + public TimeProvider? TimeProvider { get; set; } +#endif } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs index 7bb9ddcf9..998483850 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs @@ -450,7 +450,7 @@ public async Task ValidateTokenRequest_ExpiredAuthorizationCodeCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.AuthorizationCode) - .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)) + .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return default; @@ -493,7 +493,7 @@ public async Task ValidateTokenRequest_ExpiredRefreshTokenCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.RefreshToken) - .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)) + .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return default; diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs index 490e84f58..c6776c8ee 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs @@ -523,7 +523,7 @@ public async Task ValidateIntrospectionRequest_ExpiredTokenCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.RefreshToken) - .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)); + .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)); return default; }); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs index bf3b7f2a3..02257e828 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs @@ -180,7 +180,7 @@ public async Task ValidateUserinfoRequest_ExpiredTokenCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.AccessToken) - .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)); + .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)); return default; }); From 67d987431de35de486b9035d9c98384bb19d0698 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 13:22:32 +0200 Subject: [PATCH 03/30] Set TimeProvider if not set --- ...tConfigure.cs => OpenIddictCoreConfiguration.cs} | 2 +- src/OpenIddict.Core/OpenIddictCoreExtensions.cs | 2 +- .../OpenIddictQuartzConfiguration.cs | 13 ++++++++++++- src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs | 3 +++ .../OpenIddictServerConfiguration.cs | 7 +++++++ .../OpenIddictValidationConfiguration.cs | 7 +++++++ 6 files changed, 31 insertions(+), 3 deletions(-) rename src/OpenIddict.Core/{OpenIddictCoreOptionsPostConfigure.cs => OpenIddictCoreConfiguration.cs} (85%) diff --git a/src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs similarity index 85% rename from src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs rename to src/OpenIddict.Core/OpenIddictCoreConfiguration.cs index 2b44791f3..6973acfb0 100644 --- a/src/OpenIddict.Core/OpenIddictCoreOptionsPostConfigure.cs +++ b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs @@ -8,7 +8,7 @@ namespace OpenIddict.Core; -public class OpenIddictCoreOptionsPostConfigure : IPostConfigureOptions +public class OpenIddictCoreConfiguration : IPostConfigureOptions { public void PostConfigure(string? name, OpenIddictCoreOptions options) { diff --git a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs index 6d8c9d8a4..0529c9b06 100644 --- a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs @@ -48,7 +48,7 @@ public static OpenIddictCoreBuilder AddCore(this OpenIddictBuilder builder) builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, OpenIddictCoreOptionsPostConfigure>()); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, OpenIddictCoreConfiguration>()); builder.Services.TryAddScoped(static provider => { diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs index e6f9bc0cd..688e1b6ff 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs @@ -13,7 +13,8 @@ namespace OpenIddict.Quartz; /// Contains the methods required to ensure that the OpenIddict Quartz.NET configuration is valid. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictQuartzConfiguration : IConfigureOptions +public sealed class OpenIddictQuartzConfiguration : IConfigureOptions, + IPostConfigureOptions { /// public void Configure(QuartzOptions options) @@ -42,4 +43,14 @@ public void Configure(QuartzOptions options) .StartAt(DateBuilder.FutureDate(new Random().Next(1, 10), IntervalUnit.Minute)); }); } + + public void PostConfigure(string? name, OpenIddictQuartzOptions options) + { +#if SUPPORTS_TIME_PROVIDER + if (options.TimeProvider is null) + { + options.TimeProvider = TimeProvider.System; + } +#endif + } } diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs b/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs index bd80cfd3b..1f4c12c86 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs @@ -38,6 +38,9 @@ public static OpenIddictQuartzBuilder UseQuartz(this OpenIddictCoreBuilder build builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton< IConfigureOptions, OpenIddictQuartzConfiguration>()); + builder.Services.TryAddEnumerable(ServiceDescriptor.Transient< + IPostConfigureOptions, OpenIddictQuartzConfiguration>()); + return new OpenIddictQuartzBuilder(builder.Services); } diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index cdeac1840..22bdf05d4 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -29,6 +29,13 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) throw new ArgumentNullException(nameof(options)); } +#if SUPPORTS_TIME_PROVIDER + if (options.TimeProvider is null) + { + options.TimeProvider = TimeProvider.System; + } +#endif + // Explicitly disable all the features that are implicitly excluded when the degraded mode is active. if (options.EnableDegradedMode) { diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index f6fa86c82..a555b42c5 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -33,6 +33,13 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) throw new ArgumentNullException(nameof(options)); } +#if SUPPORTS_TIME_PROVIDER + if (options.TimeProvider is null) + { + options.TimeProvider = TimeProvider.System; + } +#endif + if (options.JsonWebTokenHandler is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0075)); From 6d11cb094414022d20d92ca479a4189a0b1404e1 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 13:44:43 +0200 Subject: [PATCH 04/30] Replace DateTime.Now with TimeProvider.GetUtcNow().DateTime --- .../OpenIddictClientBuilder.cs | 16 +++++++++++---- .../OpenIddictClientConfiguration.cs | 19 +++++++++++++----- .../OpenIddictClientOptions.cs | 9 ++++++++- .../OpenIddictServerBuilder.cs | 18 ++++++++++++----- .../OpenIddictServerConfiguration.cs | 20 ++++++++++--------- .../OpenIddictValidationConfiguration.cs | 16 ++++++++------- 6 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 08e7546fb..c3deb7140 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -192,14 +192,18 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate() /// Registers (and generates if necessary) a user-specific development encryption certificate. /// /// The subject name associated with the certificate. + /// The NotBefore of the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) + public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } + notBefore ??= DateTimeOffset.UtcNow; + var notBeforeDateTime = notBefore.Value.DateTime; + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -209,7 +213,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui .OfType() .ToList(); - if (!certificates.Exists(static certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -555,14 +559,18 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate() /// Registers (and generates if necessary) a user-specific development signing certificate. /// /// The subject name associated with the certificate. + /// The NotBefore of the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) + public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } + notBefore ??= DateTimeOffset.UtcNow; + var notBeforeDateTime = notBefore.Value.DateTime; + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -572,7 +580,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe .OfType() .ToList(); - if (!certificates.Exists(static certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index c0095f19b..69b6620b6 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -44,6 +44,13 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) throw new InvalidOperationException(SR.GetResourceString(SR.ID0075)); } +#if SUPPORTS_TIME_PROVIDER + if (options.TimeProvider is null) + { + options.TimeProvider = TimeProvider.System; + } +#endif + foreach (var registration in options.Registrations) { if (registration.Issuer is null) @@ -212,9 +219,11 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) // Sort the handlers collection using the order associated with each handler. options.Handlers.Sort((left, right) => left.Order.CompareTo(right.Order)); + var now = options.GetUtcNow().DateTime; + // Sort the encryption and signing credentials. - options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key)); - options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key)); + options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); + options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); // Generate a key identifier for the encryption/signing keys that don't already have one. foreach (var key in options.EncryptionCredentials.Select(credentials => credentials.Key) @@ -234,7 +243,7 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) from credentials in options.EncryptionCredentials select credentials.Key; - static int Compare(SecurityKey left, SecurityKey right) => (left, right) switch + static int Compare(SecurityKey left, SecurityKey right, DateTime now) => (left, right) switch { // If the two keys refer to the same instances, return 0. (SecurityKey first, SecurityKey second) when ReferenceEquals(first, second) => 0, @@ -245,8 +254,8 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) (SecurityKey, SymmetricSecurityKey) => 1, // If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet. - (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > DateTime.Now => 1, - (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > DateTime.Now => -1, + (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, + (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > now => -1, // If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date. (X509SecurityKey first, X509SecurityKey second) => -first.Certificate.NotAfter.CompareTo(second.Certificate.NotAfter), diff --git a/src/OpenIddict.Client/OpenIddictClientOptions.cs b/src/OpenIddict.Client/OpenIddictClientOptions.cs index 0e718f6e2..7a91031f0 100644 --- a/src/OpenIddict.Client/OpenIddictClientOptions.cs +++ b/src/OpenIddict.Client/OpenIddictClientOptions.cs @@ -14,7 +14,7 @@ namespace OpenIddict.Client; /// /// Provides various settings needed to configure the OpenIddict client handler. /// -public sealed class OpenIddictClientOptions +public sealed class OpenIddictClientOptions : IOptionWithTimeProvider { /// /// Gets or sets the optional URI used to uniquely identify the client/relying party. @@ -157,4 +157,11 @@ public sealed class OpenIddictClientOptions /// [EditorBrowsable(EditorBrowsableState.Advanced)] public HashSet ResponseTypes { get; } = new(StringComparer.Ordinal); + +#if SUPPORTS_TIME_PROVIDER + /// + /// Gets the time provider + /// + public TimeProvider? TimeProvider { get; set; } +#endif } diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 624c0e96f..392da14a3 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -201,14 +201,18 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() /// Registers (and generates if necessary) a user-specific development encryption certificate. /// /// The subject name associated with the certificate. + /// The NotBefore of the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) + public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } + notBefore ??= DateTimeOffset.UtcNow; + var notBeforeDateTime = notBefore.Value.DateTime; + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -218,7 +222,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui .OfType() .ToList(); - if (!certificates.Exists(static certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -564,14 +568,18 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate() /// Registers (and generates if necessary) a user-specific development signing certificate. /// /// The subject name associated with the certificate. + /// The NotBefore of the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) + public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } + notBefore ??= DateTimeOffset.UtcNow; + var notBeforeDateTime = notBefore.Value.DateTime; + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -581,7 +589,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe .OfType() .ToList(); - if (!certificates.Exists(static certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -1627,7 +1635,7 @@ public OpenIddictServerBuilder RegisterScopes(params string[] scopes) { throw new ArgumentNullException(nameof(scopes)); } - + if (Array.Exists(scopes, string.IsNullOrEmpty)) { throw new ArgumentException(SR.GetResourceString(SR.ID0074), nameof(scopes)); diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index 22bdf05d4..119fbf5d0 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -190,16 +190,18 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) throw new InvalidOperationException(SR.GetResourceString(SR.ID0086)); } + var now = options.GetUtcNow().DateTime; + // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. - if (options.EncryptionCredentials.TrueForAll(static credentials => credentials.Key is X509SecurityKey x509SecurityKey && - (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) + if (options.EncryptionCredentials.TrueForAll(credentials => credentials.Key is X509SecurityKey x509SecurityKey && + (x509SecurityKey.Certificate.NotBefore > now || x509SecurityKey.Certificate.NotAfter < now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0087)); } // If all the registered signing credentials are backed by a X.509 certificate, at least one of them must be valid. - if (options.SigningCredentials.TrueForAll(static credentials => credentials.Key is X509SecurityKey x509SecurityKey && - (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) + if (options.SigningCredentials.TrueForAll(credentials => credentials.Key is X509SecurityKey x509SecurityKey && + (x509SecurityKey.Certificate.NotBefore > now || x509SecurityKey.Certificate.NotAfter < now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0088)); } @@ -375,8 +377,8 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) options.Handlers.Sort(static (left, right) => left.Order.CompareTo(right.Order)); // Sort the encryption and signing credentials. - options.EncryptionCredentials.Sort(static (left, right) => Compare(left.Key, right.Key)); - options.SigningCredentials.Sort(static (left, right) => Compare(left.Key, right.Key)); + options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); + options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); // Generate a key identifier for the encryption/signing keys that don't already have one. foreach (var key in options.EncryptionCredentials.Select(credentials => credentials.Key) @@ -396,7 +398,7 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) from credentials in options.EncryptionCredentials select credentials.Key; - static int Compare(SecurityKey left, SecurityKey right) => (left, right) switch + static int Compare(SecurityKey left, SecurityKey right, DateTime now) => (left, right) switch { // If the two keys refer to the same instances, return 0. (SecurityKey first, SecurityKey second) when ReferenceEquals(first, second) => 0, @@ -407,8 +409,8 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) (SecurityKey, SymmetricSecurityKey) => 1, // If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet. - (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > DateTime.Now => 1, - (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > DateTime.Now => -1, + (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, + (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > now => -1, // If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date. (X509SecurityKey first, X509SecurityKey second) => -first.Certificate.NotAfter.CompareTo(second.Certificate.NotAfter), diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index a555b42c5..09960db5f 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -97,10 +97,12 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) } } + var now = options.GetUtcNow().DateTime; + // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.Count is not 0 && - options.EncryptionCredentials.TrueForAll(static credentials => credentials.Key is X509SecurityKey x509SecurityKey && - (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) + options.EncryptionCredentials.TrueForAll(credentials => credentials.Key is X509SecurityKey x509SecurityKey && + (x509SecurityKey.Certificate.NotBefore > now || x509SecurityKey.Certificate.NotAfter < now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0087)); } @@ -146,15 +148,15 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) options.Handlers.Sort((left, right) => left.Order.CompareTo(right.Order)); // Sort the encryption and signing credentials. - options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key)); - options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key)); + options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); + options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); // Attach the encryption credentials to the token validation parameters. options.TokenValidationParameters.TokenDecryptionKeys = from credentials in options.EncryptionCredentials select credentials.Key; - static int Compare(SecurityKey left, SecurityKey right) => (left, right) switch + static int Compare(SecurityKey left, SecurityKey right, DateTime now) => (left, right) switch { // If the two keys refer to the same instances, return 0. (SecurityKey first, SecurityKey second) when ReferenceEquals(first, second) => 0, @@ -165,8 +167,8 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) (SecurityKey, SymmetricSecurityKey) => 1, // If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet. - (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > DateTime.Now => 1, - (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > DateTime.Now => -1, + (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, + (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > now => -1, // If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date. (X509SecurityKey first, X509SecurityKey second) => -first.Certificate.NotAfter.CompareTo(second.Certificate.NotAfter), From 1262d0f99bac0fa1ec97b4453ed9121a51bb9f57 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 13:55:32 +0200 Subject: [PATCH 05/30] Fix tests --- .../OpenIddictQuartzJobTests.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs b/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs index 3c269f597..96caeef3f 100644 --- a/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs +++ b/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs @@ -28,7 +28,12 @@ public async Task Execute_UsesServiceScope() var scope = Mock.Of(scope => scope.ServiceProvider == provider); var factory = Mock.Of(factory => factory.CreateScope() == scope); var monitor = Mock.Of>( - monitor => monitor.CurrentValue == new OpenIddictQuartzOptions()); + monitor => monitor.CurrentValue == new OpenIddictQuartzOptions + { +#if SUPPORTS_TIME_PROVIDER + TimeProvider = TimeProvider.System, +#endif + }); var job = new OpenIddictQuartzJob(monitor, Mock.Of(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory)); @@ -337,6 +342,10 @@ private static OpenIddictQuartzJob CreateJob(IServiceProvider provider, OpenIddi var monitor = Mock.Of>( monitor => monitor.CurrentValue == (options ?? new OpenIddictQuartzOptions())); +#if SUPPORTS_TIME_PROVIDER + monitor.CurrentValue.TimeProvider = TimeProvider.System; +#endif + return new OpenIddictQuartzJob(monitor, Mock.Of(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory)); } From 972b4faf23f2e744bee3744ded02e5488d0429f0 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:08:36 +0200 Subject: [PATCH 06/30] Replace forgotten DateTimeOffset.UtcNow --- .../Controllers/AuthorizationController.cs | 9 ++++++-- .../Controllers/AuthorizationController.cs | 9 ++++++-- .../OpenIddictClientBuilder.cs | 4 ++-- .../OpenIddictClientHandlers.Introspection.cs | 2 +- .../OpenIddictClientHandlers.Protection.cs | 2 +- .../OpenIddictClientHandlers.cs | 22 +++++++++---------- .../OpenIddictServerBuilder.cs | 4 ++-- 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs b/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs index 82d81e8df..1088f43f8 100644 --- a/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs +++ b/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs @@ -14,10 +14,12 @@ using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; +using Microsoft.Extensions.Options; using Microsoft.Owin.Security; using OpenIddict.Abstractions; using OpenIddict.Client; using OpenIddict.Client.Owin; +using OpenIddict.Core; using OpenIddict.Sandbox.AspNet.Server.Helpers; using OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization; using OpenIddict.Server.Owin; @@ -32,17 +34,20 @@ public class AuthorizationController : Controller private readonly IOpenIddictAuthorizationManager _authorizationManager; private readonly OpenIddictClientService _clientService; private readonly IOpenIddictScopeManager _scopeManager; + private readonly IOptionsSnapshot _openIddictCoreOptions; public AuthorizationController( IOpenIddictApplicationManager applicationManager, IOpenIddictAuthorizationManager authorizationManager, OpenIddictClientService clientService, - IOpenIddictScopeManager scopeManager) + IOpenIddictScopeManager scopeManager, + IOptionsSnapshot openIddictCoreOptions) { _applicationManager = applicationManager; _authorizationManager = authorizationManager; _clientService = clientService; _scopeManager = scopeManager; + _openIddictCoreOptions = openIddictCoreOptions; } [HttpGet, Route("~/connect/authorize")] @@ -57,7 +62,7 @@ public async Task Authorize() // If the user principal can't be extracted or the cookie is too old, redirect the user to the login page. var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie); if (result?.Identity == null || (request.MaxAge != null && result.Properties?.IssuedUtc != null && - DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) + _openIddictCoreOptions.Value.GetUtcNow() - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) { // For applications that want to allow the client to select the external authentication provider // that will be used to authenticate the user, the identity_provider parameter can be used for that. diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs index fddda6ed4..c88e0ab08 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs @@ -11,11 +11,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using OpenIddict.Abstractions; using OpenIddict.Client; using OpenIddict.Client.AspNetCore; +using OpenIddict.Core; using OpenIddict.Sandbox.AspNetCore.Server.Helpers; using OpenIddict.Sandbox.AspNetCore.Server.Models; using OpenIddict.Sandbox.AspNetCore.Server.ViewModels.Authorization; @@ -32,6 +34,7 @@ public class AuthorizationController : Controller private readonly IOpenIddictScopeManager _scopeManager; private readonly SignInManager _signInManager; private readonly UserManager _userManager; + private readonly IOptionsSnapshot _openIddictCoreOptions; public AuthorizationController( IOpenIddictApplicationManager applicationManager, @@ -39,7 +42,8 @@ public class AuthorizationController : Controller OpenIddictClientService clientService, IOpenIddictScopeManager scopeManager, SignInManager signInManager, - UserManager userManager) + UserManager userManager, + IOptionsSnapshot openIddictCoreOptions) { _applicationManager = applicationManager; _authorizationManager = authorizationManager; @@ -47,6 +51,7 @@ public class AuthorizationController : Controller _scopeManager = scopeManager; _signInManager = signInManager; _userManager = userManager; + _openIddictCoreOptions = openIddictCoreOptions; } #region Authorization code, implicit and hybrid flows @@ -73,7 +78,7 @@ public async Task Authorize() var result = await HttpContext.AuthenticateAsync(); if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login) || (request.MaxAge != null && result.Properties?.IssuedUtc != null && - DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) + _openIddictCoreOptions.Value.GetUtcNow() - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) { // If the client application requested promptless authentication, // return an error indicating that the user is not logged in. diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index c3deb7140..207622a54 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -221,7 +221,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); - var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2)); + var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. @@ -588,7 +588,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); - var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2)); + var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs index 764828443..8754b2166 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs @@ -295,7 +295,7 @@ public ValueTask HandleAsync(HandleIntrospectionResponseContext context) if (long.TryParse((string?) context.Response[Claims.ExpiresAt], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) && DateTimeOffset.FromUnixTimeSeconds(value) is DateTimeOffset date && - date.Add(context.Registration.TokenValidationParameters.ClockSkew) < DateTimeOffset.UtcNow) + date.Add(context.Registration.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) { context.Reject( error: Errors.ServerError, diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs index fc4cf664c..e14b5aaa8 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs @@ -653,7 +653,7 @@ public ValueTask HandleAsync(ValidateTokenContext context) Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); - if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < DateTimeOffset.UtcNow) + if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) { context.Reject( error: Errors.InvalidToken, diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 998f43dc9..6e3da0b81 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -1495,7 +1495,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) { OpenIddictClientEndpointType.Redirection when context.ExtractFrontchannelAccessToken => ((long?) context.Request[Parameters.ExpiresIn]) is long value ? - DateTimeOffset.UtcNow.AddSeconds(value) : null, + context.Options.GetUtcNow().AddSeconds(value) : null, _ => null }; @@ -2471,7 +2471,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -2849,7 +2849,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) context.BackchannelAccessTokenExpirationDate = context.ExtractBackchannelAccessToken && - context.TokenResponse.ExpiresIn is long value ? DateTimeOffset.UtcNow.AddSeconds(value) : null; + context.TokenResponse.ExpiresIn is long value ? context.Options.GetUtcNow().AddSeconds(value) : null; context.BackchannelIdentityToken = context.ExtractBackchannelIdentityToken ? context.TokenResponse.IdToken : null; @@ -4741,7 +4741,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) // response mode as it offers a better protection for SPA applications. // Unfortunately, server-side clients like ASP.NET Core applications cannot // natively use response_mode=fragment as URI fragments are never sent to servers. - // + // // As such, this handler will not choose response_mode=fragment by default and it is // expected that specialized hosts like Blazor implement custom event handlers to // opt for fragment by default, if it is supported by the authorization server. @@ -5154,7 +5154,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) return true; }); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Principal.GetStateTokenLifetime() ?? context.Options.StateTokenLifetime; if (lifetime.HasValue) @@ -5335,7 +5335,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) // // Note: the nonce is always hashed before being sent, as recommended the specification. // See https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes for more information. - if (context.Scopes.Contains(Scopes.OpenId) && !string.IsNullOrEmpty(context.Nonce) && + if (context.Scopes.Contains(Scopes.OpenId) && !string.IsNullOrEmpty(context.Nonce) && context.ResponseType?.Split(Separators.Space) is IList types && (types.Contains(ResponseTypes.Code) || types.Contains(ResponseTypes.IdToken))) { @@ -5571,7 +5571,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -5824,7 +5824,7 @@ public ValueTask HandleAsync(ProcessChallengeContext context) // validated by default. Clients that need to deal with non-standard implementations // can use custom handlers to validate user codes that use a readable format (e.g JWT). GrantTypes.DeviceCode => (true, true, false, false), - + _ => (false, false, false, false) }; @@ -6239,7 +6239,7 @@ public ValueTask HandleAsync(ProcessIntrospectionContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -6831,7 +6831,7 @@ public ValueTask HandleAsync(ProcessRevocationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -7463,7 +7463,7 @@ public ValueTask HandleAsync(ProcessSignOutContext context) return true; }); - principal.SetCreationDate(DateTimeOffset.UtcNow); + principal.SetCreationDate(context.Options.GetUtcNow()); var lifetime = context.Principal.GetStateTokenLifetime() ?? context.Options.StateTokenLifetime; if (lifetime.HasValue) diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 392da14a3..62de5e3c7 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -230,7 +230,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); - var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2)); + var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. @@ -597,7 +597,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); - var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2)); + var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. From 7fca60e1e6acc6bcbe38a85a15332fb32869b2d7 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:13:36 +0200 Subject: [PATCH 07/30] Fix XML doc --- src/OpenIddict.Client/OpenIddictClientOptions.cs | 2 +- src/OpenIddict.Core/OpenIddictCoreConfiguration.cs | 3 +++ src/OpenIddict.Core/OpenIddictCoreOptions.cs | 2 +- src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs | 2 +- src/OpenIddict.Server/OpenIddictServerOptions.cs | 2 +- src/OpenIddict.Validation/OpenIddictValidationOptions.cs | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientOptions.cs b/src/OpenIddict.Client/OpenIddictClientOptions.cs index 7a91031f0..b62ebc97a 100644 --- a/src/OpenIddict.Client/OpenIddictClientOptions.cs +++ b/src/OpenIddict.Client/OpenIddictClientOptions.cs @@ -160,7 +160,7 @@ public sealed class OpenIddictClientOptions : IOptionWithTimeProvider #if SUPPORTS_TIME_PROVIDER /// - /// Gets the time provider + /// Gets or sets the time provider. /// public TimeProvider? TimeProvider { get; set; } #endif diff --git a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs index 6973acfb0..7112a93c3 100644 --- a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs +++ b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs @@ -8,6 +8,9 @@ namespace OpenIddict.Core; +/// +/// Contains the methods required to ensure that the OpenIddict core configuration is valid. +/// public class OpenIddictCoreConfiguration : IPostConfigureOptions { public void PostConfigure(string? name, OpenIddictCoreOptions options) diff --git a/src/OpenIddict.Core/OpenIddictCoreOptions.cs b/src/OpenIddict.Core/OpenIddictCoreOptions.cs index 998b90464..16ab60c3e 100644 --- a/src/OpenIddict.Core/OpenIddictCoreOptions.cs +++ b/src/OpenIddict.Core/OpenIddictCoreOptions.cs @@ -62,7 +62,7 @@ public sealed class OpenIddictCoreOptions : IOptionWithTimeProvider #if SUPPORTS_TIME_PROVIDER /// - /// Gets the time provider + /// Gets or sets the time provider. /// public TimeProvider? TimeProvider { get; set; } #endif diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs b/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs index 944ace034..7ffb76ea9 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs @@ -41,7 +41,7 @@ public sealed class OpenIddictQuartzOptions : IOptionWithTimeProvider #if SUPPORTS_TIME_PROVIDER /// - /// Gets the time provider + /// Gets or sets the time provider. /// public TimeProvider? TimeProvider { get; set; } #endif diff --git a/src/OpenIddict.Server/OpenIddictServerOptions.cs b/src/OpenIddict.Server/OpenIddictServerOptions.cs index 5e396b245..d099c793a 100644 --- a/src/OpenIddict.Server/OpenIddictServerOptions.cs +++ b/src/OpenIddict.Server/OpenIddictServerOptions.cs @@ -456,7 +456,7 @@ public sealed class OpenIddictServerOptions : IOptionWithTimeProvider #if SUPPORTS_TIME_PROVIDER /// - /// Gets the time provider + /// Gets or sets the time provider. /// public TimeProvider? TimeProvider { get; set; } #endif diff --git a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs index 730afe364..3f99fb475 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs @@ -184,7 +184,7 @@ public sealed class OpenIddictValidationOptions : IOptionWithTimeProvider #if SUPPORTS_TIME_PROVIDER /// - /// Gets the time provider + /// Gets or sets the time provider. /// public TimeProvider? TimeProvider { get; set; } #endif From ea0d2b7ee082a5646de4422655051c54e1ac6240 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:17:58 +0200 Subject: [PATCH 08/30] Return duplicated space --- src/OpenIddict.Client/OpenIddictClientConfiguration.cs | 2 +- src/OpenIddict.Server/OpenIddictServerConfiguration.cs | 2 +- src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 69b6620b6..9ffbe4bf0 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -254,7 +254,7 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) (SecurityKey, SymmetricSecurityKey) => 1, // If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet. - (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, + (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > now => -1, // If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date. diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index 119fbf5d0..bcb4e4d4d 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -409,7 +409,7 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) (SecurityKey, SymmetricSecurityKey) => 1, // If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet. - (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, + (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > now => -1, // If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date. diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index 09960db5f..50b1ef7ec 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -167,7 +167,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) (SecurityKey, SymmetricSecurityKey) => 1, // If one of the keys is backed by a X.509 certificate, don't prefer it if it's not valid yet. - (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, + (X509SecurityKey first, SecurityKey) when first.Certificate.NotBefore > now => 1, (SecurityKey, X509SecurityKey second) when second.Certificate.NotBefore > now => -1, // If the two keys are backed by a X.509 certificate, prefer the one with the furthest expiration date. From 51c8188aa82e9fce69d2bac5854aaa917f9312f8 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:35:40 +0200 Subject: [PATCH 09/30] Drop TimeProvider from tests --- .../OpenIddictServerIntegrationTests.Exchange.cs | 4 ++-- .../OpenIddictServerIntegrationTests.Introspection.cs | 2 +- .../OpenIddictServerIntegrationTests.Userinfo.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs index 998483850..7bb9ddcf9 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs @@ -450,7 +450,7 @@ public async Task ValidateTokenRequest_ExpiredAuthorizationCodeCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.AuthorizationCode) - .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)) + .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return default; @@ -493,7 +493,7 @@ public async Task ValidateTokenRequest_ExpiredRefreshTokenCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.RefreshToken) - .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)) + .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)) .SetClaim(Claims.Subject, "Bob le Bricoleur"); return default; diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs index c6776c8ee..490e84f58 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs @@ -523,7 +523,7 @@ public async Task ValidateIntrospectionRequest_ExpiredTokenCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.RefreshToken) - .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)); + .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)); return default; }); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs index 02257e828..bf3b7f2a3 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Userinfo.cs @@ -180,7 +180,7 @@ public async Task ValidateUserinfoRequest_ExpiredTokenCausesAnError() context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) .SetTokenType(TokenTypeHints.AccessToken) - .SetExpirationDate(context.Options.GetUtcNow() - TimeSpan.FromDays(1)); + .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1)); return default; }); From 252850b4e8260890651339783254b2d07132579c Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:45:36 +0200 Subject: [PATCH 10/30] Inline GetUtcNow extension method --- .../Controllers/AuthorizationController.cs | 8 ++- .../OpenIddictClientConfiguration.cs | 8 ++- .../OpenIddictClientHandlers.Introspection.cs | 6 +- .../OpenIddictClientHandlers.Protection.cs | 6 +- .../OpenIddictClientHandlers.cs | 48 +++++++++++--- .../OpenIddictClientOptions.cs | 2 +- .../OpenIddictAuthorizationManager.cs | 6 +- .../Managers/OpenIddictTokenManager.cs | 6 +- src/OpenIddict.Core/OpenIddictCoreOptions.cs | 2 +- src/OpenIddict.Quartz/OpenIddictQuartzJob.cs | 12 +++- .../OpenIddictQuartzOptions.cs | 2 +- .../OpenIddictServerConfiguration.cs | 8 ++- .../OpenIddictServerHandlers.Protection.cs | 12 +++- .../OpenIddictServerHandlers.cs | 66 +++++++++++++++---- .../OpenIddictServerOptions.cs | 2 +- .../OpenIddictValidationConfiguration.cs | 8 ++- ...nIddictValidationHandlers.Introspection.cs | 6 +- ...OpenIddictValidationHandlers.Protection.cs | 6 +- .../OpenIddictValidationHandlers.cs | 6 +- .../OpenIddictValidationOptions.cs | 2 +- 20 files changed, 183 insertions(+), 39 deletions(-) diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs index c88e0ab08..aa4e7f8d3 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs @@ -78,7 +78,13 @@ public async Task Authorize() var result = await HttpContext.AuthenticateAsync(); if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login) || (request.MaxAge != null && result.Properties?.IssuedUtc != null && - _openIddictCoreOptions.Value.GetUtcNow() - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) + ( +#if SUPPORTS_TIME_PROVIDER + _openIddictCoreOptions.Value.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow + ) + - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) { // If the client application requested promptless authentication, // return an error indicating that the user is not logged in. diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 9ffbe4bf0..7e58cf27d 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -219,7 +219,13 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) // Sort the handlers collection using the order associated with each handler. options.Handlers.Sort((left, right) => left.Order.CompareTo(right.Order)); - var now = options.GetUtcNow().DateTime; + var now = ( +#if SUPPORTS_TIME_PROVIDER + options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow + ) + .DateTime; // Sort the encryption and signing credentials. options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs index 8754b2166..122297527 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Introspection.cs @@ -295,7 +295,11 @@ public ValueTask HandleAsync(HandleIntrospectionResponseContext context) if (long.TryParse((string?) context.Response[Claims.ExpiresAt], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) && DateTimeOffset.FromUnixTimeSeconds(value) is DateTimeOffset date && - date.Add(context.Registration.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) + date.Add(context.Registration.TokenValidationParameters.ClockSkew) < ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)) { context.Reject( error: Errors.ServerError, diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs index e14b5aaa8..98618bacb 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs @@ -653,7 +653,11 @@ public ValueTask HandleAsync(ValidateTokenContext context) Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); - if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) + if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)) { context.Reject( error: Errors.InvalidToken, diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 6e3da0b81..79718477e 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -1495,7 +1495,11 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) { OpenIddictClientEndpointType.Redirection when context.ExtractFrontchannelAccessToken => ((long?) context.Request[Parameters.ExpiresIn]) is long value ? - context.Options.GetUtcNow().AddSeconds(value) : null, + ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow).AddSeconds(value) : null, _ => null }; @@ -2471,7 +2475,11 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -2849,7 +2857,11 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) context.BackchannelAccessTokenExpirationDate = context.ExtractBackchannelAccessToken && - context.TokenResponse.ExpiresIn is long value ? context.Options.GetUtcNow().AddSeconds(value) : null; + context.TokenResponse.ExpiresIn is long value ? ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow).AddSeconds(value) : null; context.BackchannelIdentityToken = context.ExtractBackchannelIdentityToken ? context.TokenResponse.IdToken : null; @@ -5154,7 +5166,11 @@ public ValueTask HandleAsync(ProcessChallengeContext context) return true; }); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Principal.GetStateTokenLifetime() ?? context.Options.StateTokenLifetime; if (lifetime.HasValue) @@ -5571,7 +5587,11 @@ public ValueTask HandleAsync(ProcessChallengeContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -6239,7 +6259,11 @@ public ValueTask HandleAsync(ProcessIntrospectionContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -6831,7 +6855,11 @@ public ValueTask HandleAsync(ProcessRevocationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -7463,7 +7491,11 @@ public ValueTask HandleAsync(ProcessSignOutContext context) return true; }); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Principal.GetStateTokenLifetime() ?? context.Options.StateTokenLifetime; if (lifetime.HasValue) diff --git a/src/OpenIddict.Client/OpenIddictClientOptions.cs b/src/OpenIddict.Client/OpenIddictClientOptions.cs index b62ebc97a..4b86e7c94 100644 --- a/src/OpenIddict.Client/OpenIddictClientOptions.cs +++ b/src/OpenIddict.Client/OpenIddictClientOptions.cs @@ -14,7 +14,7 @@ namespace OpenIddict.Client; /// /// Provides various settings needed to configure the OpenIddict client handler. /// -public sealed class OpenIddictClientOptions : IOptionWithTimeProvider +public sealed class OpenIddictClientOptions { /// /// Gets or sets the optional URI used to uniquely identify the client/relying party. diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index d57e8de1b..0ef781714 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -232,7 +232,11 @@ await foreach (var result in ValidateAsync(authorization, cancellationToken)) var descriptor = new OpenIddictAuthorizationDescriptor { ApplicationId = client, - CreationDate = Options.CurrentValue.GetUtcNow(), + CreationDate = +#if SUPPORTS_TIME_PROVIDER + Options.CurrentValue.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow, Principal = principal, Status = Statuses.Valid, Subject = subject, diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs index c92e77514..aba9da05b 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs @@ -1088,7 +1088,11 @@ public virtual async ValueTask TryRedeemAsync(TToken token, CancellationTo // the first time the token is redeemed. In this case, attach the current date. if (await Store.GetRedemptionDateAsync(token, cancellationToken) is null) { - await Store.SetRedemptionDateAsync(token, Options.CurrentValue.GetUtcNow(), cancellationToken); + await Store.SetRedemptionDateAsync(token, +#if SUPPORTS_TIME_PROVIDER + Options.CurrentValue.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow, cancellationToken); } await Store.SetStatusAsync(token, Statuses.Redeemed, cancellationToken); diff --git a/src/OpenIddict.Core/OpenIddictCoreOptions.cs b/src/OpenIddict.Core/OpenIddictCoreOptions.cs index 16ab60c3e..0adda6d88 100644 --- a/src/OpenIddict.Core/OpenIddictCoreOptions.cs +++ b/src/OpenIddict.Core/OpenIddictCoreOptions.cs @@ -9,7 +9,7 @@ namespace OpenIddict.Core; /// /// Provides various settings needed to configure the OpenIddict core services. /// -public sealed class OpenIddictCoreOptions : IOptionWithTimeProvider +public sealed class OpenIddictCoreOptions { /// /// Gets or sets the type corresponding to the default Application entity, diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs b/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs index a3cde99e5..9048e48aa 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzJob.cs @@ -74,7 +74,11 @@ public async Task Execute(IJobExecutionContext context) UnscheduleFiringTrigger = true }; - var threshold = _options.CurrentValue.GetUtcNow() - _options.CurrentValue.MinimumTokenLifespan; + var threshold = ( +#if SUPPORTS_TIME_PROVIDER + _options.CurrentValue.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow) - _options.CurrentValue.MinimumTokenLifespan; try { @@ -119,7 +123,11 @@ public async Task Execute(IJobExecutionContext context) UnscheduleFiringTrigger = true }; - var threshold = _options.CurrentValue.GetUtcNow() - _options.CurrentValue.MinimumAuthorizationLifespan; + var threshold = ( +#if SUPPORTS_TIME_PROVIDER + _options.CurrentValue.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow) - _options.CurrentValue.MinimumAuthorizationLifespan; try { diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs b/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs index 7ffb76ea9..8cc176594 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzOptions.cs @@ -9,7 +9,7 @@ namespace OpenIddict.Quartz; /// /// Provides various settings needed to configure the OpenIddict Quartz.NET integration. /// -public sealed class OpenIddictQuartzOptions : IOptionWithTimeProvider +public sealed class OpenIddictQuartzOptions { /// /// Gets or sets a boolean indicating whether authorizations pruning should be disabled. diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index bcb4e4d4d..2a8f2b07f 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -190,7 +190,13 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) throw new InvalidOperationException(SR.GetResourceString(SR.ID0086)); } - var now = options.GetUtcNow().DateTime; + var now = ( +#if SUPPORTS_TIME_PROVIDER + options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow + ) + .DateTime; // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.TrueForAll(credentials => credentials.Key is X509SecurityKey x509SecurityKey && diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index 8bb6eb480..07bd994ce 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -908,7 +908,11 @@ public ValueTask HandleAsync(ValidateTokenContext context) Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); - if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) + if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)) { context.Reject( error: context.Principal.GetTokenType() switch @@ -1110,7 +1114,11 @@ async ValueTask IsReusableAsync(object token) } var date = await _tokenManager.GetRedemptionDateAsync(token); - if (date is null || context.Options.GetUtcNow() < date + context.Options.RefreshTokenReuseLeeway) + if (date is null || ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow) < date + context.Options.RefreshTokenReuseLeeway) { return true; } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 27a3b261d..ffe7fcc23 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -2747,7 +2747,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) var descriptor = new OpenIddictAuthorizationDescriptor { - CreationDate = context.Options.GetUtcNow(), + CreationDate = ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow), Principal = context.Principal, Status = Statuses.Valid, Subject = context.Principal.GetClaim(Claims.Subject), @@ -2887,7 +2891,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) claim.Properties.Remove(Properties.Destinations); } - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetAccessTokenLifetime(); @@ -3011,7 +3019,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetAuthorizationCodeLifetime(); @@ -3137,7 +3149,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetDeviceCodeLifetime(); @@ -3250,7 +3266,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed // and must exactly match the expiration date of the refresh token used in the token request. @@ -3401,7 +3421,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) claim.Properties.Remove(Properties.Destinations); } - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetIdentityTokenLifetime(); @@ -3526,7 +3550,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetUserCodeLifetime(); @@ -4288,9 +4316,17 @@ public ValueTask HandleAsync(ProcessSignInContext context) { // If an expiration date was set on the access token principal, return it to the client application. if (context.AccessTokenPrincipal.GetExpirationDate() - is DateTimeOffset date && date > context.Options.GetUtcNow()) + is DateTimeOffset date && date > ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)) { - context.Response.ExpiresIn = (long) ((date - context.Options.GetUtcNow()).TotalSeconds + .5); + context.Response.ExpiresIn = (long) ((date - ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)).TotalSeconds + .5); } // If the granted access token scopes differ from the requested scopes, return the granted scopes @@ -4352,8 +4388,16 @@ public ValueTask HandleAsync(ProcessSignInContext context) { // If an expiration date was set on the device code or user // code principal, return it to the client application. - DateTimeOffset date when date > context.Options.GetUtcNow() - => (long) ((date - context.Options.GetUtcNow()).TotalSeconds + .5), + DateTimeOffset date when date > ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow) + => (long) ((date - ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)).TotalSeconds + .5), // Otherwise, return an arbitrary value, as the "expires_in" // parameter is required in device authorization responses. diff --git a/src/OpenIddict.Server/OpenIddictServerOptions.cs b/src/OpenIddict.Server/OpenIddictServerOptions.cs index d099c793a..cf9b7d7d2 100644 --- a/src/OpenIddict.Server/OpenIddictServerOptions.cs +++ b/src/OpenIddict.Server/OpenIddictServerOptions.cs @@ -13,7 +13,7 @@ namespace OpenIddict.Server; /// /// Provides various settings needed to configure the OpenIddict server handler. /// -public sealed class OpenIddictServerOptions : IOptionWithTimeProvider +public sealed class OpenIddictServerOptions { /// /// Gets or sets the optional URI used to uniquely identify the authorization server. diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index 50b1ef7ec..e6ce118f7 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -97,7 +97,13 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) } } - var now = options.GetUtcNow().DateTime; + var now = ( +#if SUPPORTS_TIME_PROVIDER + options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow + ) + .DateTime; // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.Count is not 0 && diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs index 64ef8369b..e95c12bc5 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs @@ -295,7 +295,11 @@ public ValueTask HandleAsync(HandleIntrospectionResponseContext context) if (long.TryParse((string?) context.Response[Claims.ExpiresAt], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) && DateTimeOffset.FromUnixTimeSeconds(value) is DateTimeOffset date && - date.Add(context.Options.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) + date.Add(context.Options.TokenValidationParameters.ClockSkew) < ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)) { context.Reject( error: Errors.InvalidToken, diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs index 54de6f3ad..5b6651169 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs @@ -646,7 +646,11 @@ public ValueTask HandleAsync(ValidateTokenContext context) Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); var date = context.Principal.GetExpirationDate(); - if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < context.Options.GetUtcNow()) + if (date.HasValue && date.Value.Add(context.TokenValidationParameters.ClockSkew) < ( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)) { context.Logger.LogInformation(SR.GetResourceString(SR.ID6156)); diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs index 9f35b308d..b6a7a89b7 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs @@ -361,7 +361,11 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(context.Options.GetUtcNow()); + principal.SetCreationDate(( +#if SUPPORTS_TIME_PROVIDER + context.Options.TimeProvider?.GetUtcNow() ?? +#endif + DateTimeOffset.UtcNow)); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) diff --git a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs index 3f99fb475..cb52f5b11 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs @@ -13,7 +13,7 @@ namespace OpenIddict.Validation; /// /// Provides various settings needed to configure the OpenIddict validation handler. /// -public sealed class OpenIddictValidationOptions : IOptionWithTimeProvider +public sealed class OpenIddictValidationOptions { /// /// Gets the list of encryption credentials used by the OpenIddict validation services. From c95824d01c1a2cce2d0f176a0bee102aac5f1e6e Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:49:32 +0200 Subject: [PATCH 11/30] Implement AddDevelopment*Certificate(subject, DateTimeOffset) as extension method --- .../OpenIddictClientBuilder.cs | 26 ++++++++++++++----- .../OpenIddictServerBuilder.cs | 26 ++++++++++++++----- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 207622a54..4811eddc3 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -188,21 +188,28 @@ public OpenIddictClientBuilder AddEncryptionKey(SecurityKey key) public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate() => AddDevelopmentEncryptionCertificate(new X500DistinguishedName("CN=OpenIddict Client Encryption Certificate")); + /// + /// Registers (and generates if necessary) a user-specific development encryption certificate. + /// + /// The subject name associated with the certificate. + /// The instance. + public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) => + AddDevelopmentEncryptionCertificate(subject, DateTimeOffset.UtcNow); + /// /// Registers (and generates if necessary) a user-specific development encryption certificate. /// /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) + public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - notBefore ??= DateTimeOffset.UtcNow; - var notBeforeDateTime = notBefore.Value.DateTime; + var notBeforeDateTime = notBefore.DateTime; using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -555,21 +562,28 @@ public OpenIddictClientBuilder AddSigningKey(SecurityKey key) public OpenIddictClientBuilder AddDevelopmentSigningCertificate() => AddDevelopmentSigningCertificate(new X500DistinguishedName("CN=OpenIddict Client Signing Certificate")); + /// + /// Registers (and generates if necessary) a user-specific development signing certificate. + /// + /// The subject name associated with the certificate. + /// The instance. + public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) => + AddDevelopmentSigningCertificate(subject, DateTimeOffset.UtcNow); + /// /// Registers (and generates if necessary) a user-specific development signing certificate. /// /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) + public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - notBefore ??= DateTimeOffset.UtcNow; - var notBeforeDateTime = notBefore.Value.DateTime; + var notBeforeDateTime = notBefore.DateTime; using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 62de5e3c7..3adb03d4e 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -197,21 +197,28 @@ public OpenIddictServerBuilder AddEncryptionKey(SecurityKey key) public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() => AddDevelopmentEncryptionCertificate(new X500DistinguishedName("CN=OpenIddict Server Encryption Certificate")); + /// + /// Registers (and generates if necessary) a user-specific development encryption certificate. + /// + /// The subject name associated with the certificate. + /// The instance. + public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) => + AddDevelopmentEncryptionCertificate(subject, DateTimeOffset.UtcNow); + /// /// Registers (and generates if necessary) a user-specific development encryption certificate. /// /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) + public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - notBefore ??= DateTimeOffset.UtcNow; - var notBeforeDateTime = notBefore.Value.DateTime; + var notBeforeDateTime = notBefore.DateTime; using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -564,21 +571,28 @@ public OpenIddictServerBuilder AddSigningKey(SecurityKey key) public OpenIddictServerBuilder AddDevelopmentSigningCertificate() => AddDevelopmentSigningCertificate(new X500DistinguishedName("CN=OpenIddict Server Signing Certificate")); + /// + /// Registers (and generates if necessary) a user-specific development signing certificate. + /// + /// The subject name associated with the certificate. + /// The instance. + public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) => + AddDevelopmentSigningCertificate(subject, DateTimeOffset.UtcNow); + /// /// Registers (and generates if necessary) a user-specific development signing certificate. /// /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset? notBefore = null) + public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - notBefore ??= DateTimeOffset.UtcNow; - var notBeforeDateTime = notBefore.Value.DateTime; + var notBeforeDateTime = notBefore.DateTime; using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); From eeede967c3bf7d5d6bef10ab8a1b3b3dbadfb4fe Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:51:11 +0200 Subject: [PATCH 12/30] Drop IOptionWithTimeProvider --- .../IOptionWithTimeProvider.cs | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs diff --git a/src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs b/src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs deleted file mode 100644 index 2f0cc1767..000000000 --- a/src/OpenIddict.Abstractions/IOptionWithTimeProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -namespace OpenIddict.Abstractions; - -public interface IOptionWithTimeProvider -{ -#if SUPPORTS_TIME_PROVIDER - /// - /// Gets the time provider - /// - public TimeProvider? TimeProvider { get; } -#endif -} From 3907ec3b047a4e3898758b77aad166c2f475793d Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:53:16 +0200 Subject: [PATCH 13/30] Drop TimeProviderExtensions --- .../TimeProviderExtensions.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/OpenIddict.Abstractions/TimeProviderExtensions.cs diff --git a/src/OpenIddict.Abstractions/TimeProviderExtensions.cs b/src/OpenIddict.Abstractions/TimeProviderExtensions.cs deleted file mode 100644 index 27607c24f..000000000 --- a/src/OpenIddict.Abstractions/TimeProviderExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -namespace OpenIddict.Abstractions; - -public static class TimeProviderExtensions -{ - public static DateTimeOffset GetUtcNow(this T option) where T : IOptionWithTimeProvider - { -#if SUPPORTS_TIME_PROVIDER - return option.TimeProvider!.GetUtcNow(); -#else - return DateTimeOffset.UtcNow; -#endif - } -} From 893826e925bb83a9220c958851f9da716f4876c3 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 15:53:31 +0200 Subject: [PATCH 14/30] Drop TimeProvider set in tests --- .../OpenIddictQuartzJobTests.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs b/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs index 96caeef3f..3c269f597 100644 --- a/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs +++ b/test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs @@ -28,12 +28,7 @@ public async Task Execute_UsesServiceScope() var scope = Mock.Of(scope => scope.ServiceProvider == provider); var factory = Mock.Of(factory => factory.CreateScope() == scope); var monitor = Mock.Of>( - monitor => monitor.CurrentValue == new OpenIddictQuartzOptions - { -#if SUPPORTS_TIME_PROVIDER - TimeProvider = TimeProvider.System, -#endif - }); + monitor => monitor.CurrentValue == new OpenIddictQuartzOptions()); var job = new OpenIddictQuartzJob(monitor, Mock.Of(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory)); @@ -342,10 +337,6 @@ private static OpenIddictQuartzJob CreateJob(IServiceProvider provider, OpenIddi var monitor = Mock.Of>( monitor => monitor.CurrentValue == (options ?? new OpenIddictQuartzOptions())); -#if SUPPORTS_TIME_PROVIDER - monitor.CurrentValue.TimeProvider = TimeProvider.System; -#endif - return new OpenIddictQuartzJob(monitor, Mock.Of(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory)); } From 3fa99ddcc7ae84725ba3ce1c04a7ba56cd81b669 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:10:50 +0200 Subject: [PATCH 15/30] Fix CS --- .../OpenIddictClientHandlers.cs | 45 ++++++++++--------- .../OpenIddictServerHandlers.cs | 42 ++++++++--------- .../OpenIddictValidationHandlers.cs | 6 +-- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 79718477e..1131a3643 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -1494,8 +1494,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) context.FrontchannelAccessTokenExpirationDate = context.EndpointType switch { OpenIddictClientEndpointType.Redirection when context.ExtractFrontchannelAccessToken - => ((long?) context.Request[Parameters.ExpiresIn]) is long value ? - ( + => (long?)context.Request[Parameters.ExpiresIn] is long value? ( #if SUPPORTS_TIME_PROVIDER context.Options.TimeProvider?.GetUtcNow() ?? #endif @@ -2475,11 +2474,11 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -2857,11 +2856,13 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) context.BackchannelAccessTokenExpirationDate = context.ExtractBackchannelAccessToken && - context.TokenResponse.ExpiresIn is long value ? ( + context.TokenResponse.ExpiresIn is long value + ? ( #if SUPPORTS_TIME_PROVIDER context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow).AddSeconds(value) : null; + DateTimeOffset.UtcNow).AddSeconds(value) + : null; context.BackchannelIdentityToken = context.ExtractBackchannelIdentityToken ? context.TokenResponse.IdToken : null; @@ -5166,11 +5167,11 @@ public ValueTask HandleAsync(ProcessChallengeContext context) return true; }); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Principal.GetStateTokenLifetime() ?? context.Options.StateTokenLifetime; if (lifetime.HasValue) @@ -5587,11 +5588,11 @@ public ValueTask HandleAsync(ProcessChallengeContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -6259,11 +6260,11 @@ public ValueTask HandleAsync(ProcessIntrospectionContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -6855,11 +6856,11 @@ public ValueTask HandleAsync(ProcessRevocationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) @@ -7491,11 +7492,11 @@ public ValueTask HandleAsync(ProcessSignOutContext context) return true; }); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Principal.GetStateTokenLifetime() ?? context.Options.StateTokenLifetime; if (lifetime.HasValue) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index ffe7fcc23..8e22962bf 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -2747,11 +2747,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) var descriptor = new OpenIddictAuthorizationDescriptor { - CreationDate = ( + CreationDate = #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow), + DateTimeOffset.UtcNow, Principal = context.Principal, Status = Statuses.Valid, Subject = context.Principal.GetClaim(Claims.Subject), @@ -2891,11 +2891,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) claim.Properties.Remove(Properties.Destinations); } - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetAccessTokenLifetime(); @@ -3019,11 +3019,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetAuthorizationCodeLifetime(); @@ -3149,11 +3149,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetDeviceCodeLifetime(); @@ -3266,11 +3266,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed // and must exactly match the expiration date of the refresh token used in the token request. @@ -3421,11 +3421,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) claim.Properties.Remove(Properties.Destinations); } - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetIdentityTokenLifetime(); @@ -3550,11 +3550,11 @@ public async ValueTask HandleAsync(ProcessSignInContext context) return true; }); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); // If a specific token lifetime was attached to the principal, prefer it over any other value. var lifetime = context.Principal.GetUserCodeLifetime(); diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs index b6a7a89b7..958922809 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs @@ -361,11 +361,11 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) nameType: Claims.Name, roleType: Claims.Role)); - principal.SetCreationDate(( + principal.SetCreationDate( #if SUPPORTS_TIME_PROVIDER - context.Options.TimeProvider?.GetUtcNow() ?? + context.Options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow)); + DateTimeOffset.UtcNow); var lifetime = context.Options.ClientAssertionLifetime; if (lifetime.HasValue) From 1a31bd3dadb4ef99f7098cf364fba4ba01059f53 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:21:04 +0200 Subject: [PATCH 16/30] Try to resolve TimeProvider from container --- .../OpenIddictClientConfiguration.cs | 16 +++++++++++++++- .../OpenIddictCoreConfiguration.cs | 12 +++++++++++- .../OpenIddictQuartzConfiguration.cs | 12 +++++++++++- .../OpenIddictServerConfiguration.cs | 12 +++++++++++- .../OpenIddictValidationConfiguration.cs | 16 +++++++++++++++- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 7e58cf27d..9462c166c 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -9,6 +9,7 @@ using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Tokens; @@ -23,14 +24,27 @@ namespace OpenIddict.Client; public sealed class OpenIddictClientConfiguration : IPostConfigureOptions { private readonly OpenIddictClientService _service; + private readonly IServiceProvider? _serviceProvider; /// /// Creates a new instance of the class. /// /// The OpenIddict client service. + [Obsolete($"Use constructor with the {nameof(IServiceProvider)}", false)] public OpenIddictClientConfiguration(OpenIddictClientService service) => _service = service ?? throw new ArgumentNullException(nameof(service)); + /// + /// Creates a new instance of the class. + /// + /// The OpenIddict client service. + /// The ServiceProvider. + public OpenIddictClientConfiguration(OpenIddictClientService service, IServiceProvider serviceProvider) + { + _service = service ?? throw new ArgumentNullException(nameof(service)); + _serviceProvider = serviceProvider; + } + /// public void PostConfigure(string? name, OpenIddictClientOptions options) { @@ -47,7 +61,7 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = TimeProvider.System; + options.TimeProvider = _serviceProvider?.GetService() ?? TimeProvider.System; } #endif diff --git a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs index 7112a93c3..83ed13bd0 100644 --- a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs +++ b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace OpenIddict.Core; @@ -13,12 +14,21 @@ namespace OpenIddict.Core; /// public class OpenIddictCoreConfiguration : IPostConfigureOptions { + private readonly IServiceProvider _serviceProvider; + + /// + /// Creates a new instance of the class. + /// + /// The ServiceProvider. + public OpenIddictCoreConfiguration(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + public void PostConfigure(string? name, OpenIddictCoreOptions options) { #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = TimeProvider.System; + options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; } #endif } diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs index 688e1b6ff..e640ace41 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs @@ -5,6 +5,7 @@ */ using System.ComponentModel; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace OpenIddict.Quartz; @@ -16,6 +17,15 @@ namespace OpenIddict.Quartz; public sealed class OpenIddictQuartzConfiguration : IConfigureOptions, IPostConfigureOptions { + private readonly IServiceProvider _serviceProvider; + + /// + /// Creates a new instance of the class. + /// + /// The ServiceProvider. + public OpenIddictQuartzConfiguration(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + /// public void Configure(QuartzOptions options) { @@ -49,7 +59,7 @@ public void PostConfigure(string? name, OpenIddictQuartzOptions options) #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = TimeProvider.System; + options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; } #endif } diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index 2a8f2b07f..ca473024f 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Runtime.InteropServices; using System.Text; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using OpenIddict.Extensions; @@ -21,6 +22,15 @@ namespace OpenIddict.Server; [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictServerConfiguration : IPostConfigureOptions { + private readonly IServiceProvider _serviceProvider; + + /// + /// Creates a new instance of the class. + /// + /// The ServiceProvider. + public OpenIddictServerConfiguration(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + /// public void PostConfigure(string? name, OpenIddictServerOptions options) { @@ -32,7 +42,7 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = TimeProvider.System; + options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; } #endif diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index e6ce118f7..b7bef9306 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -4,6 +4,7 @@ * the license and the contributors participating to this project. */ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Tokens; @@ -17,14 +18,27 @@ namespace OpenIddict.Validation; public sealed class OpenIddictValidationConfiguration : IPostConfigureOptions { private readonly OpenIddictValidationService _service; + private readonly IServiceProvider? _serviceProvider; /// /// Creates a new instance of the class. /// /// The validation service. + [Obsolete($"Use constructor with the {nameof(IServiceProvider)}", false)] public OpenIddictValidationConfiguration(OpenIddictValidationService service) => _service = service ?? throw new ArgumentNullException(nameof(service)); + /// + /// Creates a new instance of the class. + /// + /// The validation service. + /// The ServiceProvider. + public OpenIddictValidationConfiguration(OpenIddictValidationService service, IServiceProvider serviceProvider) + { + _service = service ?? throw new ArgumentNullException(nameof(service)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } + /// public void PostConfigure(string? name, OpenIddictValidationOptions options) { @@ -36,7 +50,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = TimeProvider.System; + options.TimeProvider = _serviceProvider?.GetService() ?? TimeProvider.System; } #endif From a0fa491e18b7c746fa20ce71c98b0cc5c42de284 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:22:22 +0200 Subject: [PATCH 17/30] Rollback samples changes --- .../Controllers/AuthorizationController.cs | 9 ++------- .../Controllers/AuthorizationController.cs | 15 ++------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs b/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs index 1088f43f8..82d81e8df 100644 --- a/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs +++ b/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs @@ -14,12 +14,10 @@ using System.Web.Mvc; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; -using Microsoft.Extensions.Options; using Microsoft.Owin.Security; using OpenIddict.Abstractions; using OpenIddict.Client; using OpenIddict.Client.Owin; -using OpenIddict.Core; using OpenIddict.Sandbox.AspNet.Server.Helpers; using OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization; using OpenIddict.Server.Owin; @@ -34,20 +32,17 @@ public class AuthorizationController : Controller private readonly IOpenIddictAuthorizationManager _authorizationManager; private readonly OpenIddictClientService _clientService; private readonly IOpenIddictScopeManager _scopeManager; - private readonly IOptionsSnapshot _openIddictCoreOptions; public AuthorizationController( IOpenIddictApplicationManager applicationManager, IOpenIddictAuthorizationManager authorizationManager, OpenIddictClientService clientService, - IOpenIddictScopeManager scopeManager, - IOptionsSnapshot openIddictCoreOptions) + IOpenIddictScopeManager scopeManager) { _applicationManager = applicationManager; _authorizationManager = authorizationManager; _clientService = clientService; _scopeManager = scopeManager; - _openIddictCoreOptions = openIddictCoreOptions; } [HttpGet, Route("~/connect/authorize")] @@ -62,7 +57,7 @@ public async Task Authorize() // If the user principal can't be extracted or the cookie is too old, redirect the user to the login page. var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie); if (result?.Identity == null || (request.MaxAge != null && result.Properties?.IssuedUtc != null && - _openIddictCoreOptions.Value.GetUtcNow() - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) + DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) { // For applications that want to allow the client to select the external authentication provider // that will be used to authenticate the user, the identity_provider parameter can be used for that. diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs index aa4e7f8d3..fddda6ed4 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Controllers/AuthorizationController.cs @@ -11,13 +11,11 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using OpenIddict.Abstractions; using OpenIddict.Client; using OpenIddict.Client.AspNetCore; -using OpenIddict.Core; using OpenIddict.Sandbox.AspNetCore.Server.Helpers; using OpenIddict.Sandbox.AspNetCore.Server.Models; using OpenIddict.Sandbox.AspNetCore.Server.ViewModels.Authorization; @@ -34,7 +32,6 @@ public class AuthorizationController : Controller private readonly IOpenIddictScopeManager _scopeManager; private readonly SignInManager _signInManager; private readonly UserManager _userManager; - private readonly IOptionsSnapshot _openIddictCoreOptions; public AuthorizationController( IOpenIddictApplicationManager applicationManager, @@ -42,8 +39,7 @@ public class AuthorizationController : Controller OpenIddictClientService clientService, IOpenIddictScopeManager scopeManager, SignInManager signInManager, - UserManager userManager, - IOptionsSnapshot openIddictCoreOptions) + UserManager userManager) { _applicationManager = applicationManager; _authorizationManager = authorizationManager; @@ -51,7 +47,6 @@ public class AuthorizationController : Controller _scopeManager = scopeManager; _signInManager = signInManager; _userManager = userManager; - _openIddictCoreOptions = openIddictCoreOptions; } #region Authorization code, implicit and hybrid flows @@ -78,13 +73,7 @@ public async Task Authorize() var result = await HttpContext.AuthenticateAsync(); if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login) || (request.MaxAge != null && result.Properties?.IssuedUtc != null && - ( -#if SUPPORTS_TIME_PROVIDER - _openIddictCoreOptions.Value.TimeProvider?.GetUtcNow() ?? -#endif - DateTimeOffset.UtcNow - ) - - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) + DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) { // If the client application requested promptless authentication, // return an error indicating that the user is not logged in. From b9e64eb79417e7d622438b917af27325de7a68b4 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:26:34 +0200 Subject: [PATCH 18/30] Use DateTime in AddDevelopment*Certificate --- .../OpenIddictClientBuilder.cs | 20 ++++++++----------- .../OpenIddictServerBuilder.cs | 20 ++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 4811eddc3..715ff324e 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -194,7 +194,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) => - AddDevelopmentEncryptionCertificate(subject, DateTimeOffset.UtcNow); + AddDevelopmentEncryptionCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development encryption certificate. @@ -202,15 +202,13 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate() /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) + public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - var notBeforeDateTime = notBefore.DateTime; - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -220,7 +218,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui .OfType() .ToList(); - if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -228,7 +226,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); - var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. @@ -568,7 +566,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) => - AddDevelopmentSigningCertificate(subject, DateTimeOffset.UtcNow); + AddDevelopmentSigningCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development signing certificate. @@ -576,15 +574,13 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate() /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) + public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - var notBeforeDateTime = notBefore.DateTime; - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -594,7 +590,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe .OfType() .ToList(); - if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -602,7 +598,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); - var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 3adb03d4e..3eaecd437 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -203,7 +203,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) => - AddDevelopmentEncryptionCertificate(subject, DateTimeOffset.UtcNow); + AddDevelopmentEncryptionCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development encryption certificate. @@ -211,15 +211,13 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) + public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - var notBeforeDateTime = notBefore.DateTime; - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -229,7 +227,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui .OfType() .ToList(); - if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -237,7 +235,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); - var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. @@ -577,7 +575,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) => - AddDevelopmentSigningCertificate(subject, DateTimeOffset.UtcNow); + AddDevelopmentSigningCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development signing certificate. @@ -585,15 +583,13 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate() /// The subject name associated with the certificate. /// The NotBefore of the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTimeOffset notBefore) + public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - var notBeforeDateTime = notBefore.DateTime; - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -603,7 +599,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe .OfType() .ToList(); - if (!certificates.Exists(certificate => certificate.NotBefore < notBeforeDateTime && certificate.NotAfter > notBeforeDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -611,7 +607,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); - var certificate = request.CreateSelfSigned(notBeforeDateTime, notBeforeDateTime.AddYears(2)); + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. From 5d130ba33dc23a0340a34653fa45de7ead865b53 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:28:31 +0200 Subject: [PATCH 19/30] Fix OpenIddictQuartzConfigurationTests --- .../OpenIddictQuartzConfigurationTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/OpenIddict.Quartz.Tests/OpenIddictQuartzConfigurationTests.cs b/test/OpenIddict.Quartz.Tests/OpenIddictQuartzConfigurationTests.cs index ce37626f3..2b0d5a507 100644 --- a/test/OpenIddict.Quartz.Tests/OpenIddictQuartzConfigurationTests.cs +++ b/test/OpenIddict.Quartz.Tests/OpenIddictQuartzConfigurationTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.DependencyInjection; using Quartz; using Xunit; @@ -10,7 +11,7 @@ public void UseQuartz_RegistersJobDetails() { // Arrange var options = new QuartzOptions(); - var configuration = new OpenIddictQuartzConfiguration(); + var configuration = new OpenIddictQuartzConfiguration(new ServiceCollection().BuildServiceProvider()); // Act configuration.Configure(options); @@ -28,7 +29,7 @@ public void UseQuartz_RegistersTriggerDetails() { // Arrange var options = new QuartzOptions(); - var configuration = new OpenIddictQuartzConfiguration(); + var configuration = new OpenIddictQuartzConfiguration(new ServiceCollection().BuildServiceProvider()); // Act configuration.Configure(options); From 83a86c2930716fdd9ec15245e8a33f39f31ea0dd Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:37:11 +0200 Subject: [PATCH 20/30] Fix CS --- src/OpenIddict.Client/OpenIddictClientBuilder.cs | 8 ++++---- src/OpenIddict.Server/OpenIddictServerBuilder.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 715ff324e..86a8edba8 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -193,8 +193,8 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate() /// /// The subject name associated with the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) => - AddDevelopmentEncryptionCertificate(subject, DateTime.Now); + public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) + => AddDevelopmentEncryptionCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development encryption certificate. @@ -565,8 +565,8 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate() /// /// The subject name associated with the certificate. /// The instance. - public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) => - AddDevelopmentSigningCertificate(subject, DateTime.Now); + public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) + => AddDevelopmentSigningCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development signing certificate. diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 3eaecd437..6314eb09a 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -202,8 +202,8 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() /// /// The subject name associated with the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) => - AddDevelopmentEncryptionCertificate(subject, DateTime.Now); + public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) + => AddDevelopmentEncryptionCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development encryption certificate. @@ -574,8 +574,8 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate() /// /// The subject name associated with the certificate. /// The instance. - public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) => - AddDevelopmentSigningCertificate(subject, DateTime.Now); + public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) + => AddDevelopmentSigningCertificate(subject, DateTime.Now); /// /// Registers (and generates if necessary) a user-specific development signing certificate. From 055732048d9cdda1d73a03d8c0478295e623f2dd Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:46:43 +0200 Subject: [PATCH 21/30] Use TimeProvider in AddDevelopment*Certificate --- .../OpenIddictClientBuilder.cs | 231 ++++++++++-------- .../OpenIddictServerBuilder.cs | 231 ++++++++++-------- 2 files changed, 246 insertions(+), 216 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 86a8edba8..e61cb192f 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -194,82 +194,90 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) - => AddDevelopmentEncryptionCertificate(subject, DateTime.Now); - - /// - /// Registers (and generates if necessary) a user-specific development encryption certificate. - /// - /// The subject name associated with the certificate. - /// The NotBefore of the certificate. - /// The instance. - public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadWrite); - - // Try to retrieve the existing development certificates from the specified store. - // If no valid existing certificate was found, create a new encryption certificate. - var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) - .OfType() - .ToList(); - - if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + Services.AddOptions().Configure((options, serviceProvider) => { -#if SUPPORTS_CERTIFICATE_GENERATION - using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); +#if SUPPORTS_TIME_PROVIDER + var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); + var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; +#else + var notBefore = DateTime.Now; +#endif - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadWrite); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + // Try to retrieve the existing development certificates from the specified store. + // If no valid existing certificate was found, create a new encryption certificate. + var certificates = store.Certificates + .Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) + .OfType() + .ToList(); - // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). - // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!certificates.Exists(certificate => + certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { - certificate.FriendlyName = "OpenIddict Client Development Encryption Certificate"; - } +#if SUPPORTS_CERTIFICATE_GENERATION + using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate - // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. - // To work around this issue, the certificate payload is manually exported and imported back - // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. - var data = certificate.Export(X509ContentType.Pfx, string.Empty); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, + critical: true)); - try - { - var flags = X509KeyStorageFlags.PersistKeySet; + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); - // Note: macOS requires marking the certificate private key as exportable. - // If this flag is not set, a CryptographicException is thrown at runtime. - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). + // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - flags |= X509KeyStorageFlags.Exportable; + certificate.FriendlyName = "OpenIddict Client Development Encryption Certificate"; } - certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); - } + // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate + // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. + // To work around this issue, the certificate payload is manually exported and imported back + // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. + var data = certificate.Export(X509ContentType.Pfx, string.Empty); - finally - { - Array.Clear(data, 0, data.Length); - } + try + { + var flags = X509KeyStorageFlags.PersistKeySet; - store.Add(certificate); + // Note: macOS requires marking the certificate private key as exportable. + // If this flag is not set, a CryptographicException is thrown at runtime. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + flags |= X509KeyStorageFlags.Exportable; + } + + certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); + } + + finally + { + Array.Clear(data, 0, data.Length); + } + + store.Add(certificate); #else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); + throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); #endif - } + } - return Configure(options => options.EncryptionCredentials.AddRange( - from certificate in certificates - let key = new X509SecurityKey(certificate) - select new EncryptingCredentials(key, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512))); + options.EncryptionCredentials.AddRange( + from certificate in certificates + let key = new X509SecurityKey(certificate) + select new EncryptingCredentials(key, SecurityAlgorithms.RsaOAEP, + SecurityAlgorithms.Aes256CbcHmacSha512)); + }); + + return this; } /// @@ -566,82 +574,89 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) - => AddDevelopmentSigningCertificate(subject, DateTime.Now); - - /// - /// Registers (and generates if necessary) a user-specific development signing certificate. - /// - /// The subject name associated with the certificate. - /// The NotBefore of the certificate. - /// The instance. - public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadWrite); - - // Try to retrieve the existing development certificates from the specified store. - // If no valid existing certificate was found, create a new signing certificate. - var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) - .OfType() - .ToList(); - - if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + Services.AddOptions().Configure((options, serviceProvider) => { -#if SUPPORTS_CERTIFICATE_GENERATION - using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); +#if SUPPORTS_TIME_PROVIDER + var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); + var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; +#else + var notBefore = DateTime.Now; +#endif - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadWrite); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + // Try to retrieve the existing development certificates from the specified store. + // If no valid existing certificate was found, create a new signing certificate. + var certificates = store.Certificates + .Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) + .OfType() + .ToList(); - // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). - // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!certificates.Exists(certificate => + certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { - certificate.FriendlyName = "OpenIddict Client Development Signing Certificate"; - } +#if SUPPORTS_CERTIFICATE_GENERATION + using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate - // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. - // To work around this issue, the certificate payload is manually exported and imported back - // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. - var data = certificate.Export(X509ContentType.Pfx, string.Empty); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, + critical: true)); - try - { - var flags = X509KeyStorageFlags.PersistKeySet; + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); - // Note: macOS requires marking the certificate private key as exportable. - // If this flag is not set, a CryptographicException is thrown at runtime. - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). + // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - flags |= X509KeyStorageFlags.Exportable; + certificate.FriendlyName = "OpenIddict Client Development Signing Certificate"; } - certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); - } + // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate + // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. + // To work around this issue, the certificate payload is manually exported and imported back + // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. + var data = certificate.Export(X509ContentType.Pfx, string.Empty); - finally - { - Array.Clear(data, 0, data.Length); - } + try + { + var flags = X509KeyStorageFlags.PersistKeySet; - store.Add(certificate); + // Note: macOS requires marking the certificate private key as exportable. + // If this flag is not set, a CryptographicException is thrown at runtime. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + flags |= X509KeyStorageFlags.Exportable; + } + + certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); + } + + finally + { + Array.Clear(data, 0, data.Length); + } + + store.Add(certificate); #else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); + throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); #endif - } + } - return Configure(options => options.SigningCredentials.AddRange( - from certificate in certificates - let key = new X509SecurityKey(certificate) - select new SigningCredentials(key, SecurityAlgorithms.RsaSha256))); + options.SigningCredentials.AddRange( + from certificate in certificates + let key = new X509SecurityKey(certificate) + select new SigningCredentials(key, SecurityAlgorithms.RsaSha256)); + }); + + return this; } /// diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 6314eb09a..e50744a5b 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -203,82 +203,90 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject) - => AddDevelopmentEncryptionCertificate(subject, DateTime.Now); - - /// - /// Registers (and generates if necessary) a user-specific development encryption certificate. - /// - /// The subject name associated with the certificate. - /// The NotBefore of the certificate. - /// The instance. - public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadWrite); - - // Try to retrieve the existing development certificates from the specified store. - // If no valid existing certificate was found, create a new encryption certificate. - var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) - .OfType() - .ToList(); - - if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + Services.AddOptions().Configure((options, serviceProvider) => { -#if SUPPORTS_CERTIFICATE_GENERATION - using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); +#if SUPPORTS_TIME_PROVIDER + var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); + var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; +#else + var notBefore = DateTime.Now; +#endif - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadWrite); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + // Try to retrieve the existing development certificates from the specified store. + // If no valid existing certificate was found, create a new encryption certificate. + var certificates = store.Certificates + .Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) + .OfType() + .ToList(); - // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). - // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!certificates.Exists(certificate => + certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { - certificate.FriendlyName = "OpenIddict Server Development Encryption Certificate"; - } +#if SUPPORTS_CERTIFICATE_GENERATION + using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate - // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. - // To work around this issue, the certificate payload is manually exported and imported back - // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. - var data = certificate.Export(X509ContentType.Pfx, string.Empty); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, + critical: true)); - try - { - var flags = X509KeyStorageFlags.PersistKeySet; + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); - // Note: macOS requires marking the certificate private key as exportable. - // If this flag is not set, a CryptographicException is thrown at runtime. - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). + // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - flags |= X509KeyStorageFlags.Exportable; + certificate.FriendlyName = "OpenIddict Server Development Encryption Certificate"; } - certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); - } + // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate + // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. + // To work around this issue, the certificate payload is manually exported and imported back + // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. + var data = certificate.Export(X509ContentType.Pfx, string.Empty); - finally - { - Array.Clear(data, 0, data.Length); - } + try + { + var flags = X509KeyStorageFlags.PersistKeySet; - store.Add(certificate); + // Note: macOS requires marking the certificate private key as exportable. + // If this flag is not set, a CryptographicException is thrown at runtime. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + flags |= X509KeyStorageFlags.Exportable; + } + + certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); + } + + finally + { + Array.Clear(data, 0, data.Length); + } + + store.Add(certificate); #else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); + throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); #endif - } + } - return Configure(options => options.EncryptionCredentials.AddRange( - from certificate in certificates - let key = new X509SecurityKey(certificate) - select new EncryptingCredentials(key, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512))); + options.EncryptionCredentials.AddRange( + from certificate in certificates + let key = new X509SecurityKey(certificate) + select new EncryptingCredentials(key, SecurityAlgorithms.RsaOAEP, + SecurityAlgorithms.Aes256CbcHmacSha512)); + }); + + return this; } /// @@ -575,82 +583,89 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate() /// The subject name associated with the certificate. /// The instance. public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject) - => AddDevelopmentSigningCertificate(subject, DateTime.Now); - - /// - /// Registers (and generates if necessary) a user-specific development signing certificate. - /// - /// The subject name associated with the certificate. - /// The NotBefore of the certificate. - /// The instance. - public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500DistinguishedName subject, DateTime notBefore) { if (subject is null) { throw new ArgumentNullException(nameof(subject)); } - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); - store.Open(OpenFlags.ReadWrite); - - // Try to retrieve the existing development certificates from the specified store. - // If no valid existing certificate was found, create a new signing certificate. - var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) - .OfType() - .ToList(); - - if (!certificates.Exists(certificate => certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + Services.AddOptions().Configure((options, serviceProvider) => { -#if SUPPORTS_CERTIFICATE_GENERATION - using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); +#if SUPPORTS_TIME_PROVIDER + var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); + var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; +#else + var notBefore = DateTime.Now; +#endif - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); + using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadWrite); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + // Try to retrieve the existing development certificates from the specified store. + // If no valid existing certificate was found, create a new signing certificate. + var certificates = store.Certificates + .Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) + .OfType() + .ToList(); - // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). - // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!certificates.Exists(certificate => + certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) { - certificate.FriendlyName = "OpenIddict Server Development Signing Certificate"; - } +#if SUPPORTS_CERTIFICATE_GENERATION + using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate - // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. - // To work around this issue, the certificate payload is manually exported and imported back - // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. - var data = certificate.Export(X509ContentType.Pfx, string.Empty); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, + critical: true)); - try - { - var flags = X509KeyStorageFlags.PersistKeySet; + var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); - // Note: macOS requires marking the certificate private key as exportable. - // If this flag is not set, a CryptographicException is thrown at runtime. - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). + // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - flags |= X509KeyStorageFlags.Exportable; + certificate.FriendlyName = "OpenIddict Server Development Signing Certificate"; } - certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); - } + // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate + // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. + // To work around this issue, the certificate payload is manually exported and imported back + // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. + var data = certificate.Export(X509ContentType.Pfx, string.Empty); - finally - { - Array.Clear(data, 0, data.Length); - } + try + { + var flags = X509KeyStorageFlags.PersistKeySet; - store.Add(certificate); + // Note: macOS requires marking the certificate private key as exportable. + // If this flag is not set, a CryptographicException is thrown at runtime. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + flags |= X509KeyStorageFlags.Exportable; + } + + certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags)); + } + + finally + { + Array.Clear(data, 0, data.Length); + } + + store.Add(certificate); #else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); + throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264)); #endif - } + } - return Configure(options => options.SigningCredentials.AddRange( - from certificate in certificates - let key = new X509SecurityKey(certificate) - select new SigningCredentials(key, SecurityAlgorithms.RsaSha256))); + options.SigningCredentials.AddRange( + from certificate in certificates + let key = new X509SecurityKey(certificate) + select new SigningCredentials(key, SecurityAlgorithms.RsaSha256)); + }); + + return this; } /// From f4ef6805c038124ed417caf5188f8ca67589f6d5 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 16:51:45 +0200 Subject: [PATCH 22/30] Use DateTimeOffset.LocalDateTime --- src/OpenIddict.Client/OpenIddictClientBuilder.cs | 12 ++++++------ src/OpenIddict.Server/OpenIddictServerBuilder.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index e61cb192f..3458992df 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -204,9 +204,9 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; + var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTime.Now; + var notBefore = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -220,7 +220,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -584,9 +584,9 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; + var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTime.Now; + var notBefore = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -600,7 +600,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index e50744a5b..1521ddb90 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -213,9 +213,9 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; + var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTime.Now; + var notBefore = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -229,7 +229,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -593,9 +593,9 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow().DateTime ?? DateTime.Now; + var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTime.Now; + var notBefore = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -609,7 +609,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore && certificate.NotAfter > notBefore)) + certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); From 28321dced73047a426a9d0786b711eede9bf063b Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 17:07:53 +0200 Subject: [PATCH 23/30] Fix AddDevelopmentEncryptionCertificate_ThrowsAnExceptionOnUnsupportedPlatforms, AddDevelopmentSigningCertificate_ThrowsAnExceptionOnUnsupportedPlatforms --- .../OpenIddictServerBuilderTests.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs index d4d5a6d73..0166da525 100644 --- a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs +++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs @@ -303,9 +303,15 @@ public void AddDevelopmentEncryptionCertificate_ThrowsAnExceptionOnUnsupportedPl var services = CreateServices(); var builder = CreateBuilder(services); + builder.AddDevelopmentEncryptionCertificate( + subject: new X500DistinguishedName("CN=" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))); + + var serviceProvider = services.BuildServiceProvider(); + + var options = serviceProvider.GetRequiredService>(); + // Act and assert - var exception = Assert.Throws(() => builder.AddDevelopmentEncryptionCertificate( - subject: new X500DistinguishedName("CN=" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)))); + var exception = Assert.Throws(() => options.Value); Assert.Equal("X.509 certificate generation is not supported on this platform.", exception.Message); } @@ -353,9 +359,15 @@ public void AddDevelopmentSigningCertificate_ThrowsAnExceptionOnUnsupportedPlatf var services = CreateServices(); var builder = CreateBuilder(services); + builder.AddDevelopmentSigningCertificate( + subject: new X500DistinguishedName("CN=" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))); + + var serviceProvider = services.BuildServiceProvider(); + + var options = serviceProvider.GetRequiredService>(); + // Act and assert - var exception = Assert.Throws(() => builder.AddDevelopmentSigningCertificate( - subject: new X500DistinguishedName("CN=" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)))); + var exception = Assert.Throws(() => options.Value); Assert.Equal("X.509 certificate generation is not supported on this platform.", exception.Message); } From a8e0fa709cc5600db70e36e9c54a990f16bb73be Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 18:35:33 +0200 Subject: [PATCH 24/30] Add spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kévin Chalet --- src/OpenIddict.Client/OpenIddictClientHandlers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 1131a3643..235ce0523 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -1494,7 +1494,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context) context.FrontchannelAccessTokenExpirationDate = context.EndpointType switch { OpenIddictClientEndpointType.Redirection when context.ExtractFrontchannelAccessToken - => (long?)context.Request[Parameters.ExpiresIn] is long value? ( + => (long?) context.Request[Parameters.ExpiresIn] is long value ? ( #if SUPPORTS_TIME_PROVIDER context.Options.TimeProvider?.GetUtcNow() ?? #endif From 6195f803ddab64662cd4bc4ca128cbdc575492da Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 18:26:02 +0200 Subject: [PATCH 25/30] Use now instead of notBefore --- src/OpenIddict.Client/OpenIddictClientBuilder.cs | 16 ++++++++-------- src/OpenIddict.Server/OpenIddictServerBuilder.cs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 3458992df..5796caf08 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -204,9 +204,9 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTimeOffset.Now; + var now = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -220,7 +220,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) + certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -230,7 +230,7 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + var certificate = request.CreateSelfSigned(now, now.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. @@ -584,9 +584,9 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTimeOffset.Now; + var now = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -600,7 +600,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) + certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -610,7 +610,7 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + var certificate = request.CreateSelfSigned(now, now.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index 1521ddb90..ee67a6b7e 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -213,9 +213,9 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTimeOffset.Now; + var now = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -229,7 +229,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) + certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -239,7 +239,7 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + var certificate = request.CreateSelfSigned(now, now.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. @@ -593,9 +593,9 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var notBefore = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; #else - var notBefore = DateTimeOffset.Now; + var now = DateTimeOffset.Now; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -609,7 +609,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe .ToList(); if (!certificates.Exists(certificate => - certificate.NotBefore < notBefore.LocalDateTime && certificate.NotAfter > notBefore.LocalDateTime)) + certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); @@ -619,7 +619,7 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); - var certificate = request.CreateSelfSigned(notBefore, notBefore.AddYears(2)); + var certificate = request.CreateSelfSigned(now, now.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. From b163d3bc3936f828ef30782e4fba09ef62e9cbca Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 18:35:06 +0200 Subject: [PATCH 26/30] Make IServiceProvider non-nullable --- src/OpenIddict.Client/OpenIddictClientConfiguration.cs | 7 ++++--- .../OpenIddictValidationConfiguration.cs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 9462c166c..1b3706730 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; @@ -24,7 +25,7 @@ namespace OpenIddict.Client; public sealed class OpenIddictClientConfiguration : IPostConfigureOptions { private readonly OpenIddictClientService _service; - private readonly IServiceProvider? _serviceProvider; + private readonly IServiceProvider _serviceProvider; /// /// Creates a new instance of the class. @@ -32,7 +33,7 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptionsThe OpenIddict client service. [Obsolete($"Use constructor with the {nameof(IServiceProvider)}", false)] public OpenIddictClientConfiguration(OpenIddictClientService service) - => _service = service ?? throw new ArgumentNullException(nameof(service)); + => throw new NotSupportedException ($"Use constructor with the {nameof(IServiceProvider)}"); /// /// Creates a new instance of the class. @@ -61,7 +62,7 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = _serviceProvider?.GetService() ?? TimeProvider.System; + options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; } #endif diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index b7bef9306..5af2ed088 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -18,7 +18,7 @@ namespace OpenIddict.Validation; public sealed class OpenIddictValidationConfiguration : IPostConfigureOptions { private readonly OpenIddictValidationService _service; - private readonly IServiceProvider? _serviceProvider; + private readonly IServiceProvider _serviceProvider; /// /// Creates a new instance of the class. @@ -26,7 +26,7 @@ public sealed class OpenIddictValidationConfiguration : IPostConfigureOptionsThe validation service. [Obsolete($"Use constructor with the {nameof(IServiceProvider)}", false)] public OpenIddictValidationConfiguration(OpenIddictValidationService service) - => _service = service ?? throw new ArgumentNullException(nameof(service)); + => throw new NotSupportedException ($"Use constructor with the {nameof(IServiceProvider)}"); /// /// Creates a new instance of the class. @@ -50,7 +50,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) #if SUPPORTS_TIME_PROVIDER if (options.TimeProvider is null) { - options.TimeProvider = _serviceProvider?.GetService() ?? TimeProvider.System; + options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; } #endif From 44eecb5f15c9df6f989ebb729888ca4f63232cba Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 18:36:16 +0200 Subject: [PATCH 27/30] Fix XML doc --- src/OpenIddict.Client/OpenIddictClientConfiguration.cs | 2 +- src/OpenIddict.Core/OpenIddictCoreConfiguration.cs | 2 +- src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs | 2 +- src/OpenIddict.Server/OpenIddictServerConfiguration.cs | 2 +- src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 1b3706730..0e905cec1 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -39,7 +39,7 @@ public OpenIddictClientConfiguration(OpenIddictClientService service) /// Creates a new instance of the class. /// /// The OpenIddict client service. - /// The ServiceProvider. + /// The service provider. public OpenIddictClientConfiguration(OpenIddictClientService service, IServiceProvider serviceProvider) { _service = service ?? throw new ArgumentNullException(nameof(service)); diff --git a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs index 83ed13bd0..428f84339 100644 --- a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs +++ b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs @@ -19,7 +19,7 @@ public class OpenIddictCoreConfiguration : IPostConfigureOptions /// Creates a new instance of the class. /// - /// The ServiceProvider. + /// The service provider. public OpenIddictCoreConfiguration(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs index e640ace41..0da7f6ea9 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs @@ -22,7 +22,7 @@ public sealed class OpenIddictQuartzConfiguration : IConfigureOptions /// Creates a new instance of the class. /// - /// The ServiceProvider. + /// The service provider. public OpenIddictQuartzConfiguration(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index ca473024f..b9c060cfd 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -27,7 +27,7 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions /// Creates a new instance of the class. /// - /// The ServiceProvider. + /// The service provider. public OpenIddictServerConfiguration(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index 5af2ed088..a3e0f684a 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -32,7 +32,7 @@ public OpenIddictValidationConfiguration(OpenIddictValidationService service) /// Creates a new instance of the class. /// /// The validation service. - /// The ServiceProvider. + /// The service provider. public OpenIddictValidationConfiguration(OpenIddictValidationService service, IServiceProvider serviceProvider) { _service = service ?? throw new ArgumentNullException(nameof(service)); From 8c24ddcc905b343ddacdc35bcb8b1c1c6943de44 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 18:38:00 +0200 Subject: [PATCH 28/30] Add missing inheritdoc --- src/OpenIddict.Core/OpenIddictCoreConfiguration.cs | 1 + src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs index 428f84339..19c0560c8 100644 --- a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs +++ b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs @@ -23,6 +23,7 @@ public class OpenIddictCoreConfiguration : IPostConfigureOptions _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + /// public void PostConfigure(string? name, OpenIddictCoreOptions options) { #if SUPPORTS_TIME_PROVIDER diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs index 0da7f6ea9..bcb1b9ae3 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs @@ -54,6 +54,7 @@ public void Configure(QuartzOptions options) }); } + /// public void PostConfigure(string? name, OpenIddictQuartzOptions options) { #if SUPPORTS_TIME_PROVIDER From 3716666bf4f37f20b42d278f657f169509c3ac54 Mon Sep 17 00:00:00 2001 From: Jan Trejbal Date: Fri, 10 May 2024 18:55:29 +0200 Subject: [PATCH 29/30] Use .LocalDateTime fot working with certificate --- src/OpenIddict.Client/OpenIddictClientBuilder.cs | 8 ++++---- src/OpenIddict.Client/OpenIddictClientConfiguration.cs | 2 +- src/OpenIddict.Server/OpenIddictServerBuilder.cs | 8 ++++---- src/OpenIddict.Server/OpenIddictServerConfiguration.cs | 2 +- .../OpenIddictValidationConfiguration.cs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index 5796caf08..b07a4fdeb 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -204,9 +204,9 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else - var now = DateTimeOffset.Now; + var now = DateTimeOffset.UtcNow; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -584,9 +584,9 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else - var now = DateTimeOffset.Now; + var now = DateTimeOffset.UtcNow; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 0e905cec1..667f00111 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -240,7 +240,7 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) #endif DateTimeOffset.UtcNow ) - .DateTime; + .LocalDateTime; // Sort the encryption and signing credentials. options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index ee67a6b7e..fdbf18e47 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -213,9 +213,9 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else - var now = DateTimeOffset.Now; + var now = DateTimeOffset.UtcNow; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); @@ -593,9 +593,9 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe { #if SUPPORTS_TIME_PROVIDER var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetLocalNow() ?? DateTimeOffset.Now; + var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else - var now = DateTimeOffset.Now; + var now = DateTimeOffset.UtcNow; #endif using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index b9c060cfd..d249e7b09 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -206,7 +206,7 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) #endif DateTimeOffset.UtcNow ) - .DateTime; + .LocalDateTime; // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.TrueForAll(credentials => credentials.Key is X509SecurityKey x509SecurityKey && diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index a3e0f684a..7f586332c 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -117,7 +117,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) #endif DateTimeOffset.UtcNow ) - .DateTime; + .LocalDateTime; // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.Count is not 0 && From 77f0cad15d742a40a7608f34c03c48b9ca0aca53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sun, 12 May 2024 17:16:47 +0200 Subject: [PATCH 30/30] Add a parameterless constructor to OpenIddictQuartzConfiguration and OpenIddictServerConfiguration and fix inconsistencies --- .../OpenIddictClientBuilder.cs | 30 +++++++------------ .../OpenIddictClientConfiguration.cs | 24 +++++++-------- .../OpenIddictCoreConfiguration.cs | 13 ++++---- .../OpenIddictCoreExtensions.cs | 6 ++-- .../OpenIddictQuartzConfiguration.cs | 22 +++++++------- .../OpenIddictQuartzExtensions.cs | 12 ++++---- .../OpenIddictServerBuilder.cs | 27 ++++++----------- .../OpenIddictServerConfiguration.cs | 19 +++++++----- .../OpenIddictValidationConfiguration.cs | 19 +++++------- 9 files changed, 75 insertions(+), 97 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index b07a4fdeb..f9baae2b2 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -200,15 +200,13 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui throw new ArgumentNullException(nameof(subject)); } - Services.AddOptions().Configure((options, serviceProvider) => + Services.AddOptions().Configure((options, provider) => { #if SUPPORTS_TIME_PROVIDER - var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; + var now = (options.TimeProvider ?? provider.GetService())?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else var now = DateTimeOffset.UtcNow; #endif - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -219,16 +217,13 @@ public OpenIddictClientBuilder AddDevelopmentEncryptionCertificate(X500Distingui .OfType() .ToList(); - if (!certificates.Exists(certificate => - certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, - RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, - critical: true)); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); var certificate = request.CreateSelfSigned(now, now.AddYears(2)); @@ -580,15 +575,13 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe throw new ArgumentNullException(nameof(subject)); } - Services.AddOptions().Configure((options, serviceProvider) => + Services.AddOptions().Configure((options, provider) => { #if SUPPORTS_TIME_PROVIDER - var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; + var now = (options.TimeProvider ?? provider.GetService())?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else var now = DateTimeOffset.UtcNow; #endif - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -599,16 +592,13 @@ public OpenIddictClientBuilder AddDevelopmentSigningCertificate(X500Distinguishe .OfType() .ToList(); - if (!certificates.Exists(certificate => - certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, - RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, - critical: true)); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); var certificate = request.CreateSelfSigned(now, now.AddYears(2)); diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 667f00111..24f446112 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -25,25 +25,25 @@ namespace OpenIddict.Client; public sealed class OpenIddictClientConfiguration : IPostConfigureOptions { private readonly OpenIddictClientService _service; - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// /// The OpenIddict client service. - [Obsolete($"Use constructor with the {nameof(IServiceProvider)}", false)] + [Obsolete("This constructor is no longer supported and will be removed in a future version.", error: true)] public OpenIddictClientConfiguration(OpenIddictClientService service) - => throw new NotSupportedException ($"Use constructor with the {nameof(IServiceProvider)}"); + => throw new NotSupportedException(SR.GetResourceString(SR.ID0403)); /// /// Creates a new instance of the class. /// + /// The service provider. /// The OpenIddict client service. - /// The service provider. - public OpenIddictClientConfiguration(OpenIddictClientService service, IServiceProvider serviceProvider) + public OpenIddictClientConfiguration(IServiceProvider provider, OpenIddictClientService service) { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); _service = service ?? throw new ArgumentNullException(nameof(service)); - _serviceProvider = serviceProvider; } /// @@ -60,10 +60,7 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) } #if SUPPORTS_TIME_PROVIDER - if (options.TimeProvider is null) - { - options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; - } + options.TimeProvider ??= _provider.GetService() ?? TimeProvider.System; #endif foreach (var registration in options.Registrations) @@ -236,11 +233,10 @@ public void PostConfigure(string? name, OpenIddictClientOptions options) var now = ( #if SUPPORTS_TIME_PROVIDER - options.TimeProvider?.GetUtcNow() ?? + options.TimeProvider?.GetUtcNow() ?? #endif - DateTimeOffset.UtcNow - ) - .LocalDateTime; + DateTimeOffset.UtcNow + ).LocalDateTime; // Sort the encryption and signing credentials. options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key, now)); diff --git a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs index 19c0560c8..2c655e495 100644 --- a/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs +++ b/src/OpenIddict.Core/OpenIddictCoreConfiguration.cs @@ -14,23 +14,20 @@ namespace OpenIddict.Core; /// public class OpenIddictCoreConfiguration : IPostConfigureOptions { - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// - /// The service provider. - public OpenIddictCoreConfiguration(IServiceProvider serviceProvider) - => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + /// The service provider. + public OpenIddictCoreConfiguration(IServiceProvider provider) + => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); /// public void PostConfigure(string? name, OpenIddictCoreOptions options) { #if SUPPORTS_TIME_PROVIDER - if (options.TimeProvider is null) - { - options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; - } + options.TimeProvider ??= _provider.GetService() ?? TimeProvider.System; #endif } } diff --git a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs index 0529c9b06..7c85a5ea1 100644 --- a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs @@ -48,8 +48,6 @@ public static OpenIddictCoreBuilder AddCore(this OpenIddictBuilder builder) builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, OpenIddictCoreConfiguration>()); - builder.Services.TryAddScoped(static provider => { var type = provider.GetRequiredService>() @@ -90,6 +88,10 @@ public static OpenIddictCoreBuilder AddCore(this OpenIddictBuilder builder) typeof(OpenIddictTokenManager<>).MakeGenericType(type)); }); + // Note: TryAddEnumerable() is used here to ensure the initializer is registered only once. + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton< + IPostConfigureOptions, OpenIddictCoreConfiguration>()); + return new OpenIddictCoreBuilder(builder.Services); } diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs index bcb1b9ae3..ccd9c113e 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs @@ -14,17 +14,22 @@ namespace OpenIddict.Quartz; /// Contains the methods required to ensure that the OpenIddict Quartz.NET configuration is valid. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictQuartzConfiguration : IConfigureOptions, - IPostConfigureOptions +public sealed class OpenIddictQuartzConfiguration : IConfigureOptions, IPostConfigureOptions { - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// - /// The service provider. - public OpenIddictQuartzConfiguration(IServiceProvider serviceProvider) - => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + [Obsolete("This constructor is no longer supported and will be removed in a future version.", error: true)] + public OpenIddictQuartzConfiguration() => throw new NotSupportedException(SR.GetResourceString(SR.ID0403)); + + /// + /// Creates a new instance of the class. + /// + /// The service provider. + public OpenIddictQuartzConfiguration(IServiceProvider provider) + => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); /// public void Configure(QuartzOptions options) @@ -58,10 +63,7 @@ public void Configure(QuartzOptions options) public void PostConfigure(string? name, OpenIddictQuartzOptions options) { #if SUPPORTS_TIME_PROVIDER - if (options.TimeProvider is null) - { - options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; - } + options.TimeProvider ??= _provider.GetService() ?? TimeProvider.System; #endif } } diff --git a/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs b/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs index 1f4c12c86..c42e96ab2 100644 --- a/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs +++ b/src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs @@ -34,12 +34,12 @@ public static OpenIddictQuartzBuilder UseQuartz(this OpenIddictCoreBuilder build // Quartz.NET's DI integration to resolve it from the DI. builder.Services.TryAddTransient(); - // Note: TryAddEnumerable() is used here to ensure the initializer is registered only once. - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton< - IConfigureOptions, OpenIddictQuartzConfiguration>()); - - builder.Services.TryAddEnumerable(ServiceDescriptor.Transient< - IPostConfigureOptions, OpenIddictQuartzConfiguration>()); + // Note: TryAddEnumerable() is used here to ensure the initializers are registered only once. + builder.Services.TryAddEnumerable( + [ + ServiceDescriptor.Singleton, OpenIddictQuartzConfiguration>(), + ServiceDescriptor.Singleton, OpenIddictQuartzConfiguration>() + ]); return new OpenIddictQuartzBuilder(builder.Services); } diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index fdbf18e47..6e15a62a0 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -209,15 +209,13 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui throw new ArgumentNullException(nameof(subject)); } - Services.AddOptions().Configure((options, serviceProvider) => + Services.AddOptions().Configure((options, provider) => { #if SUPPORTS_TIME_PROVIDER - var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; + var now = (options.TimeProvider ?? provider.GetService())?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else var now = DateTimeOffset.UtcNow; #endif - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -228,16 +226,13 @@ public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate(X500Distingui .OfType() .ToList(); - if (!certificates.Exists(certificate => - certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) + if (!certificates.Exists(certificate => certificate.NotBefore < now.LocalDateTime && certificate.NotAfter > now.LocalDateTime)) { #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, - RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, - critical: true)); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); var certificate = request.CreateSelfSigned(now, now.AddYears(2)); @@ -589,15 +584,13 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe throw new ArgumentNullException(nameof(subject)); } - Services.AddOptions().Configure((options, serviceProvider) => + Services.AddOptions().Configure((options, provider) => { #if SUPPORTS_TIME_PROVIDER - var timeProvider = options.TimeProvider ?? serviceProvider.GetService(); - var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; + var now = (options.TimeProvider ?? provider.GetService())?.GetUtcNow() ?? DateTimeOffset.UtcNow; #else var now = DateTimeOffset.UtcNow; #endif - using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); @@ -614,10 +607,8 @@ public OpenIddictServerBuilder AddDevelopmentSigningCertificate(X500Distinguishe #if SUPPORTS_CERTIFICATE_GENERATION using var algorithm = OpenIddictHelpers.CreateRsaKey(size: 2048); - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, - RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, - critical: true)); + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); var certificate = request.CreateSelfSigned(now, now.AddYears(2)); diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index d249e7b09..970b56c10 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -22,14 +22,20 @@ namespace OpenIddict.Server; [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictServerConfiguration : IPostConfigureOptions { - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// - /// The service provider. - public OpenIddictServerConfiguration(IServiceProvider serviceProvider) - => _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + [Obsolete("This constructor is no longer supported and will be removed in a future version.", error: true)] + public OpenIddictServerConfiguration() => throw new NotSupportedException(SR.GetResourceString(SR.ID0403)); + + /// + /// Creates a new instance of the class. + /// + /// The service provider. + public OpenIddictServerConfiguration(IServiceProvider provider) + => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); /// public void PostConfigure(string? name, OpenIddictServerOptions options) @@ -40,10 +46,7 @@ public void PostConfigure(string? name, OpenIddictServerOptions options) } #if SUPPORTS_TIME_PROVIDER - if (options.TimeProvider is null) - { - options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; - } + options.TimeProvider ??= _provider.GetService() ?? TimeProvider.System; #endif // Explicitly disable all the features that are implicitly excluded when the degraded mode is active. diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index 7f586332c..ff4ffba49 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -18,25 +18,25 @@ namespace OpenIddict.Validation; public sealed class OpenIddictValidationConfiguration : IPostConfigureOptions { private readonly OpenIddictValidationService _service; - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// /// The validation service. - [Obsolete($"Use constructor with the {nameof(IServiceProvider)}", false)] + [Obsolete("This constructor is no longer supported and will be removed in a future version.", error: true)] public OpenIddictValidationConfiguration(OpenIddictValidationService service) - => throw new NotSupportedException ($"Use constructor with the {nameof(IServiceProvider)}"); + => throw new NotSupportedException(SR.GetResourceString(SR.ID0403)); /// /// Creates a new instance of the class. /// + /// The service provider. /// The validation service. - /// The service provider. - public OpenIddictValidationConfiguration(OpenIddictValidationService service, IServiceProvider serviceProvider) + public OpenIddictValidationConfiguration(IServiceProvider provider, OpenIddictValidationService service) { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); _service = service ?? throw new ArgumentNullException(nameof(service)); - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); } /// @@ -48,10 +48,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) } #if SUPPORTS_TIME_PROVIDER - if (options.TimeProvider is null) - { - options.TimeProvider = _serviceProvider.GetService() ?? TimeProvider.System; - } + options.TimeProvider ??= _provider.GetService() ?? TimeProvider.System; #endif if (options.JsonWebTokenHandler is null) @@ -59,7 +56,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options) throw new InvalidOperationException(SR.GetResourceString(SR.ID0075)); } - if (options.Configuration is null && options.ConfigurationManager is null && + if (options.Configuration is null && options.ConfigurationManager is null && options.Issuer is null && options.ConfigurationEndpoint is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0128));