From fff7694505d1d3e7704e13076ec1d4f6657d963d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 1 Jun 2026 07:12:45 +0200 Subject: [PATCH] Decouple the ASP.NET Core/OWIN integration options from the authentication scheme options --- .../Web.config | 6 --- .../Web.config | 6 --- .../OpenIddictResources.resx | 20 +++---- .../OpenIddictClientAspNetCoreHandler.cs | 4 +- .../OpenIddictClientAspNetCoreOptions.cs | 2 +- .../OpenIddictClientOwinConfiguration.cs | 5 -- .../OpenIddictClientOwinExtensions.cs | 1 + .../OpenIddictClientOwinHandler.cs | 47 +++++++++++------ .../OpenIddictClientOwinMiddleware.cs | 52 ++++++++++++------- .../OpenIddictClientOwinMiddlewareFactory.cs | 13 +---- .../OpenIddictClientOwinOptions.cs | 9 +--- .../OpenIddictServerAspNetCoreHandler.cs | 4 +- .../OpenIddictServerAspNetCoreOptions.cs | 2 +- .../OpenIddictServerOwinConfiguration.cs | 18 +------ .../OpenIddictServerOwinExtensions.cs | 4 +- .../OpenIddictServerOwinHandler.cs | 2 +- .../OpenIddictServerOwinMiddleware.cs | 39 ++++++++------ .../OpenIddictServerOwinMiddlewareFactory.cs | 13 +---- .../OpenIddictServerOwinOptions.cs | 9 +--- .../OpenIddictValidationAspNetCoreHandler.cs | 4 +- .../OpenIddictValidationAspNetCoreOptions.cs | 2 +- .../OpenIddictValidationOwinExtensions.cs | 1 + .../OpenIddictValidationOwinHandler.cs | 2 +- .../OpenIddictValidationOwinMiddleware.cs | 43 +++++++++------ ...enIddictValidationOwinMiddlewareFactory.cs | 13 +---- .../OpenIddictValidationOwinOptions.cs | 11 ++-- 26 files changed, 147 insertions(+), 185 deletions(-) diff --git a/sandbox/OpenIddict.Sandbox.AspNet.Client/Web.config b/sandbox/OpenIddict.Sandbox.AspNet.Client/Web.config index f0f038e8..16a80ae5 100644 --- a/sandbox/OpenIddict.Sandbox.AspNet.Client/Web.config +++ b/sandbox/OpenIddict.Sandbox.AspNet.Client/Web.config @@ -100,12 +100,6 @@ - - - - - - diff --git a/sandbox/OpenIddict.Sandbox.AspNet.Server/Web.config b/sandbox/OpenIddict.Sandbox.AspNet.Server/Web.config index 2d0685a8..d9053fb0 100644 --- a/sandbox/OpenIddict.Sandbox.AspNet.Server/Web.config +++ b/sandbox/OpenIddict.Sandbox.AspNet.Server/Web.config @@ -124,12 +124,6 @@ - - - - - - diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 3d1d310d..d8933e84 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -497,10 +497,6 @@ This may indicate that the event handler responsible for processing OpenID Conne Only strings, booleans, integers, arrays of strings and instances of type 'OpenIddictParameter', 'JsonElement' or derived from 'JsonNode' can be returned as custom parameters. - - The OpenIddict OWIN server handler cannot be used as an active authentication handler. -Make sure that 'OpenIddictServerOwinOptions.AuthenticationMode' is not set to 'Active'. - The OWIN request cannot be resolved. @@ -510,8 +506,8 @@ For the OpenIddict server services to work correctly, a per-request 'IServicePro Note: when using a dependency injection container supporting middleware resolution (like Autofac), the 'app.UseOpenIddictServer()' extension MUST NOT be called. - The OpenIddict server services cannot be resolved from the DI container. -To register the server services, use 'services.AddOpenIddict().AddServer()'. + The authentication handler used by the OpenIddict server components cannot be resolved from the DI container. +To register the OWIN integration, use 'services.AddOpenIddict().AddServer().UseOwin()'. Audiences cannot be null or empty. @@ -702,8 +698,8 @@ For the OpenIddict validation services to work correctly, a per-request 'IServic Note: when using a dependency injection container supporting middleware resolution (like Autofac), the 'app.UseOpenIddictValidation()' extension MUST NOT be called. - The OpenIddict validation services cannot be resolved from the DI container. -To register the validation services, use 'services.AddOpenIddict().AddValidation()'. + The authentication handler used by the OpenIddict validation components cannot be resolved from the DI container. +To register the OWIN integration, use 'services.AddOpenIddict().AddValidation().UseOwin()'. The local server integration can only be used with direct validation. @@ -1097,10 +1093,6 @@ This may indicate that it was not properly registered in the dependency injectio A discovery client must be registered when using server discovery. Reference the 'OpenIddict.Client.SystemNetHttp' package and call 'services.AddOpenIddict().AddClient().UseSystemNetHttp()' to register the default System.Net.Http-based integration. - - - The OpenIddict OWIN client handler cannot be used as an active authentication handler. -Make sure that 'OpenIddictClientOwinOptions.AuthenticationMode' is not set to 'Active'. An error occurred while retrieving the OpenIddict client context. On ASP.NET Core, this may indicate that the authentication middleware was not registered early enough in the request pipeline. Make sure that 'app.UseAuthentication()' is registered before 'app.UseAuthorization()' and 'app.UseEndpoints()' (or 'app.UseMvc()') and try again. @@ -1111,8 +1103,8 @@ For the OpenIddict client services to work correctly, a per-request 'IServicePro Note: when using a dependency injection container supporting middleware resolution (like Autofac), the 'app.UseOpenIddictClient()' extension MUST NOT be called. - The OpenIddict client services cannot be resolved from the DI container. -To register the client services, use 'services.AddOpenIddict().AddClient()'. + The authentication handler used by the OpenIddict client components cannot be resolved from the DI container. +To register the OWIN integration, use 'services.AddOpenIddict().AddClient().UseOwin()'. The core services must be registered when enabling the OpenIddict client feature. diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs index 3dda2386..fa5a5c3f 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs @@ -19,7 +19,7 @@ namespace OpenIddict.Client.AspNetCore; /// Provides the logic necessary to extract, validate and handle OpenID Connect requests. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler, +public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler, IAuthenticationRequestHandler, IAuthenticationSignOutHandler { @@ -32,7 +32,7 @@ public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler options, + IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreOptions.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreOptions.cs index 6c273823..1d417892 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreOptions.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreOptions.cs @@ -11,7 +11,7 @@ namespace OpenIddict.Client.AspNetCore; /// /// Provides various settings needed to configure the OpenIddict ASP.NET Core client integration. /// -public sealed class OpenIddictClientAspNetCoreOptions : AuthenticationSchemeOptions +public sealed class OpenIddictClientAspNetCoreOptions { /// /// Gets or sets a boolean indicating whether the static client registrations with a non-null diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs index 5670226a..16a54024 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs @@ -100,11 +100,6 @@ public sealed class OpenIddictClientOwinConfiguration : IConfigureOptions(); builder.Services.TryAddScoped(); // Register the built-in event handlers used by the OpenIddict OWIN client components. diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs index 52be258f..35ff8ad6 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs @@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; using System.Security.Claims; +using Microsoft.Extensions.Options; using Microsoft.Owin.Security.Infrastructure; using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants; using Properties = OpenIddict.Client.Owin.OpenIddictClientOwinConstants.Properties; @@ -20,22 +21,26 @@ namespace OpenIddict.Client.Owin; /// Provides the entry point necessary to register the OpenIddict client in an OWIN pipeline. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictClientOwinHandler : AuthenticationHandler +public sealed class OpenIddictClientOwinHandler : AuthenticationHandler { private readonly IOpenIddictClientDispatcher _dispatcher; private readonly IOpenIddictClientFactory _factory; + private readonly IOptionsMonitor _options; /// /// Creates a new instance of the class. /// /// The OpenIddict client dispatcher used by this instance. /// The OpenIddict client factory used by this instance. + /// The OpenIddict client OWIN options. public OpenIddictClientOwinHandler( IOpenIddictClientDispatcher dispatcher, - IOpenIddictClientFactory factory) + IOpenIddictClientFactory factory, + IOptionsMonitor options) { _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); _factory = factory ?? throw new ArgumentNullException(nameof(factory)); + _options = options ?? throw new ArgumentNullException(nameof(options)); } /// @@ -280,10 +285,14 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler(typeof(OpenIddictClientTransaction).FullName) ?? @@ -327,7 +336,9 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler(typeof(OpenIddictClientTransaction).FullName) ?? @@ -371,7 +382,8 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler descriptions) { // Note: unlike its server counterpart, the OpenIddict OWIN client authentication handler allows // associating additional authentication types to trigger a provider-specific challenge. For that, @@ -379,14 +391,14 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler 0 } types) + if (manager.AuthenticationResponseChallenge?.AuthenticationTypes is { Length: > 0 } types) { foreach (var type in types) { - if (TryGetForwardedAuthenticationType(type, out _)) + if (TryGetForwardedAuthenticationType(descriptions, type, out _)) { // Ensure no client registration information was attached to the authentication properties. - if (Context.Authentication.AuthenticationResponseChallenge.Properties is AuthenticationProperties properties && + if (manager.AuthenticationResponseChallenge.Properties is AuthenticationProperties properties && (properties.Dictionary.ContainsKey(Properties.Issuer) || properties.Dictionary.ContainsKey(Properties.ProviderName) || properties.Dictionary.ContainsKey(Properties.RegistrationId))) @@ -397,7 +409,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler( - Context.Authentication.AuthenticationResponseChallenge.Properties.Dictionary ?? + manager.AuthenticationResponseChallenge.Properties.Dictionary ?? ImmutableDictionary.Create()) { [Properties.ProviderName] = type @@ -409,7 +421,8 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler descriptions) { // Note: unlike its server counterpart, the OpenIddict OWIN client authentication handler allows // associating additional authentication types to trigger a provider-specific sign-out. For that, @@ -417,14 +430,14 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler 0 } types) + if (manager.AuthenticationResponseRevoke?.AuthenticationTypes is { Length: > 0 } types) { foreach (var type in types) { - if (TryGetForwardedAuthenticationType(type, out _)) + if (TryGetForwardedAuthenticationType(descriptions, type, out _)) { // Ensure no client registration information was attached to the authentication properties. - if (Context.Authentication.AuthenticationResponseRevoke.Properties is AuthenticationProperties properties && + if (manager.AuthenticationResponseRevoke.Properties is AuthenticationProperties properties && (properties.Dictionary.ContainsKey(Properties.Issuer) || properties.Dictionary.ContainsKey(Properties.ProviderName) || properties.Dictionary.ContainsKey(Properties.RegistrationId))) @@ -435,7 +448,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler( - Context.Authentication.AuthenticationResponseRevoke.Properties.Dictionary ?? + manager.AuthenticationResponseRevoke.Properties.Dictionary ?? ImmutableDictionary.Create()) { [Properties.ProviderName] = type @@ -448,10 +461,12 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler descriptions, + string type, [NotNullWhen(true)] out AuthenticationDescription? result) { - foreach (var description in Options.ForwardedAuthenticationTypes) + for (var index = 0; index < descriptions.Count; index++) { + var description = descriptions[index]; if (string.Equals(description.AuthenticationType, type, StringComparison.Ordinal)) { result = description; diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs index a7276307..f4ac9216 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Security.Claims; using System.Security.Principal; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.Owin.Security.Infrastructure; @@ -32,34 +33,30 @@ using AuthenticateDelegate = Func< /// it is NOT recommended to instantiate it as a singleton like a regular OWIN middleware. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware +public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware { - private readonly IOpenIddictClientDispatcher _dispatcher; - private readonly IOpenIddictClientFactory _factory; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// /// The next middleware in the pipeline, if applicable. - /// The OpenIddict client OWIN options. - /// The OpenIddict client dispatcher. - /// The OpenIddict client factory. + /// The service provider. public OpenIddictClientOwinMiddleware( OwinMiddleware? next, - IOptionsMonitor options, - IOpenIddictClientDispatcher dispatcher, - IOpenIddictClientFactory factory) - : base(next, options.CurrentValue) - { - _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); - _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - } + IServiceProvider provider) + : base(next, new InternalOptions()) + => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); /// public override async Task Invoke(IOwinContext context) { ArgumentNullException.ThrowIfNull(context); + // Resolve the list of forwarded authentication types from the options. + var options = _provider.GetService>() + ?.CurrentValue ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0316)); + // Retrieve the existing authentication delegate. var function = context.Get("security.Authenticate"); @@ -73,7 +70,7 @@ public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware descriptions, + string type, [NotNullWhen(true)] out AuthenticationDescription? result) { - foreach (var description in Options.ForwardedAuthenticationTypes) + for (var index = 0; index < descriptions.Count; index++) { + var description = descriptions[index]; if (string.Equals(description.AuthenticationType, type, StringComparison.Ordinal)) { result = description; @@ -154,6 +153,19 @@ public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware instance. /// /// A new instance of the class. - protected override AuthenticationHandler CreateHandler() - => new OpenIddictClientOwinHandler(_dispatcher, _factory); + protected override AuthenticationHandler CreateHandler() + => _provider.GetService() + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0317)); + + /// + /// Provides the options used by the class. + /// + private sealed class InternalOptions : AuthenticationOptions + { + /// + /// Creates a new instance of the class. + /// + public InternalOptions() : base(OpenIddictClientOwinDefaults.AuthenticationType) + => AuthenticationMode = AuthenticationMode.Passive; + } } diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddlewareFactory.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddlewareFactory.cs index 8fb1e11f..776ef7e5 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddlewareFactory.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddlewareFactory.cs @@ -5,8 +5,6 @@ */ using System.ComponentModel; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; namespace OpenIddict.Client.Owin; @@ -39,22 +37,15 @@ public sealed class OpenIddictClientOwinMiddlewareFactory : OwinMiddleware { ArgumentNullException.ThrowIfNull(context); - var provider = context.Get(typeof(IServiceProvider).FullName) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0316)); - // Note: the Microsoft.Extensions.DependencyInjection container doesn't support resolving services // with arbitrary parameters, which prevents the client OWIN middleware from being resolved directly // from the DI container, as the next middleware in the pipeline cannot be specified as a parameter. // To work around this limitation, the client OWIN middleware is manually instantiated and invoked. var middleware = new OpenIddictClientOwinMiddleware( next: Next, - options: GetRequiredService>(provider), - dispatcher: GetRequiredService(provider), - factory: GetRequiredService(provider)); + provider: context.Get(typeof(IServiceProvider).FullName) + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0316))); return middleware.Invoke(context); - - static T GetRequiredService(IServiceProvider provider) => provider.GetService() ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0317)); } } diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinOptions.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinOptions.cs index 8442594b..3f85a999 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinOptions.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinOptions.cs @@ -11,15 +11,8 @@ namespace OpenIddict.Client.Owin; /// /// Provides various settings needed to configure the OpenIddict OWIN client integration. /// -public sealed class OpenIddictClientOwinOptions : AuthenticationOptions +public sealed class OpenIddictClientOwinOptions { - /// - /// Creates a new instance of the class. - /// - public OpenIddictClientOwinOptions() - : base(OpenIddictClientOwinDefaults.AuthenticationType) - => AuthenticationMode = AuthenticationMode.Passive; - /// /// Gets or sets a boolean indicating whether the static client registrations with a non-null /// provider name attached are automatically added to . diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs index 31018c57..c8e8c3e0 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs @@ -18,7 +18,7 @@ namespace OpenIddict.Server.AspNetCore; /// Provides the logic necessary to extract, validate and handle OpenID Connect requests. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler, +public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler, IAuthenticationRequestHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler @@ -32,7 +32,7 @@ public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler options, + IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreOptions.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreOptions.cs index ab0a613e..aac6e238 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreOptions.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreOptions.cs @@ -11,7 +11,7 @@ namespace OpenIddict.Server.AspNetCore; /// /// Provides various settings needed to configure the OpenIddict ASP.NET Core server integration. /// -public sealed class OpenIddictServerAspNetCoreOptions : AuthenticationSchemeOptions +public sealed class OpenIddictServerAspNetCoreOptions { /// /// Gets or sets a boolean indicating whether incoming requests arriving on insecure endpoints should be rejected. diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs index 491a1c2f..dd11c9bf 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs @@ -14,8 +14,7 @@ namespace OpenIddict.Server.Owin; /// [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictServerOwinConfiguration : IConfigureOptions, - IPostConfigureOptions, - IValidateOptions + IPostConfigureOptions { /// public void Configure(OpenIddictServerOptions options) @@ -46,19 +45,4 @@ public sealed class OpenIddictServerOwinConfiguration : IConfigureOptions - public ValidateOptionsResult Validate(string? name, OpenIddictServerOwinOptions options) - { - ArgumentNullException.ThrowIfNull(options); - - var builder = new ValidateOptionsResultBuilder(); - - if (options.AuthenticationMode is AuthenticationMode.Active) - { - builder.AddError(SR.GetResourceString(SR.ID0119)); - } - - return builder.Build(); - } } diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs index cf2e637f..98ee11c6 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs @@ -31,6 +31,7 @@ public static class OpenIddictServerOwinExtensions // Note: unlike regular OWIN middleware, the OpenIddict server middleware is registered // as a scoped service in the DI container. This allows containers that support middleware // resolution (like Autofac) to use it without requiring additional configuration. + builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); // Register the built-in event handlers used by the OpenIddict OWIN server components. @@ -55,9 +56,6 @@ public static class OpenIddictServerOwinExtensions builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton< IPostConfigureOptions, OpenIddictServerOwinConfiguration>()); - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton< - IValidateOptions, OpenIddictServerOwinConfiguration>()); - return new OpenIddictServerOwinBuilder(builder.Services); } diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs index 69cfe2b0..f62b3c93 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs @@ -16,7 +16,7 @@ namespace OpenIddict.Server.Owin; /// Provides the entry point necessary to register the OpenIddict server in an OWIN pipeline. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictServerOwinHandler : AuthenticationHandler +public sealed class OpenIddictServerOwinHandler : AuthenticationHandler { private readonly IOpenIddictServerDispatcher _dispatcher; private readonly IOpenIddictServerFactory _factory; diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs index 0823737d..1cece580 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs @@ -5,7 +5,7 @@ */ using System.ComponentModel; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Owin.Security.Infrastructure; namespace OpenIddict.Server.Owin; @@ -17,33 +17,38 @@ namespace OpenIddict.Server.Owin; /// it is NOT recommended to instantiate it as a singleton like a regular OWIN middleware. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictServerOwinMiddleware : AuthenticationMiddleware +public sealed class OpenIddictServerOwinMiddleware : AuthenticationMiddleware { - private readonly IOpenIddictServerDispatcher _dispatcher; - private readonly IOpenIddictServerFactory _factory; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// /// The next middleware in the pipeline, if applicable. - /// The OpenIddict server OWIN options. - /// The OpenIddict server dispatcher. - /// The OpenIddict server factory. + /// The service provider. public OpenIddictServerOwinMiddleware( OwinMiddleware? next, - IOptionsMonitor options, - IOpenIddictServerDispatcher dispatcher, - IOpenIddictServerFactory factory) - : base(next, options.CurrentValue) - { - _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); - _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - } + IServiceProvider provider) + : base(next, new InternalOptions()) + => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); /// /// Creates and returns a new instance. /// /// A new instance of the class. - protected override AuthenticationHandler CreateHandler() - => new OpenIddictServerOwinHandler(_dispatcher, _factory); + protected override AuthenticationHandler CreateHandler() + => _provider.GetService() + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0122)); + + /// + /// Provides the options used by the class. + /// + private sealed class InternalOptions : AuthenticationOptions + { + /// + /// Creates a new instance of the class. + /// + public InternalOptions() : base(OpenIddictServerOwinDefaults.AuthenticationType) + => AuthenticationMode = AuthenticationMode.Passive; + } } diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddlewareFactory.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddlewareFactory.cs index 547b1e89..0f78c2bf 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddlewareFactory.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddlewareFactory.cs @@ -5,8 +5,6 @@ */ using System.ComponentModel; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; namespace OpenIddict.Server.Owin; @@ -39,22 +37,15 @@ public sealed class OpenIddictServerOwinMiddlewareFactory : OwinMiddleware { ArgumentNullException.ThrowIfNull(context); - var provider = context.Get(typeof(IServiceProvider).FullName) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0121)); - // Note: the Microsoft.Extensions.DependencyInjection container doesn't support resolving services // with arbitrary parameters, which prevents the server OWIN middleware from being resolved directly // from the DI container, as the next middleware in the pipeline cannot be specified as a parameter. // To work around this limitation, the server OWIN middleware is manually instantiated and invoked. var middleware = new OpenIddictServerOwinMiddleware( next: Next, - options: GetRequiredService>(provider), - dispatcher: GetRequiredService(provider), - factory: GetRequiredService(provider)); + provider: context.Get(typeof(IServiceProvider).FullName) + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0121))); return middleware.Invoke(context); - - static T GetRequiredService(IServiceProvider provider) => provider.GetService() ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0122)); } } diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinOptions.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinOptions.cs index 2e8fdc86..acc1938f 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinOptions.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinOptions.cs @@ -11,15 +11,8 @@ namespace OpenIddict.Server.Owin; /// /// Provides various settings needed to configure the OpenIddict OWIN server integration. /// -public sealed class OpenIddictServerOwinOptions : AuthenticationOptions +public sealed class OpenIddictServerOwinOptions { - /// - /// Creates a new instance of the class. - /// - public OpenIddictServerOwinOptions() - : base(OpenIddictServerOwinDefaults.AuthenticationType) - => AuthenticationMode = AuthenticationMode.Passive; - /// /// Gets or sets a boolean indicating whether incoming requests arriving on insecure endpoints should be rejected. /// By default, this property is set to to help mitigate man-in-the-middle attacks. diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs index ecc6818f..6c0e7148 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs @@ -18,7 +18,7 @@ namespace OpenIddict.Validation.AspNetCore; /// Provides the logic necessary to extract, validate and handle OpenID Connect requests. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandler, +public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandler, IAuthenticationRequestHandler { private readonly IOpenIddictValidationDispatcher _dispatcher; @@ -30,7 +30,7 @@ public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandle public OpenIddictValidationAspNetCoreHandler( IOpenIddictValidationDispatcher dispatcher, IOpenIddictValidationFactory factory, - IOptionsMonitor options, + IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreOptions.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreOptions.cs index 62b3d7b0..faba12d2 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreOptions.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreOptions.cs @@ -9,7 +9,7 @@ namespace OpenIddict.Validation.AspNetCore; /// /// Provides various settings needed to configure the OpenIddict ASP.NET Core validation integration. /// -public sealed class OpenIddictValidationAspNetCoreOptions : AuthenticationSchemeOptions +public sealed class OpenIddictValidationAspNetCoreOptions { /// /// Gets or sets a boolean indicating whether the built-in logic extracting diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs index e330061f..1266cb9e 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs @@ -29,6 +29,7 @@ public static class OpenIddictValidationOwinExtensions // Note: unlike regular OWIN middleware, the OpenIddict validation middleware is registered // as a scoped service in the DI container. This allows containers that support middleware // resolution (like Autofac) to use it without requiring additional configuration. + builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); // Register the built-in event handlers used by the OpenIddict OWIN validation components. diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs index 575bdf4d..5725d352 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs @@ -16,7 +16,7 @@ namespace OpenIddict.Validation.Owin; /// Provides the entry point necessary to register the OpenIddict validation in an OWIN pipeline. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler +public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler { private readonly IOpenIddictValidationDispatcher _dispatcher; private readonly IOpenIddictValidationFactory _factory; diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs index 77525510..28ed56db 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs @@ -5,6 +5,7 @@ */ using System.ComponentModel; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.Owin.Security.Infrastructure; @@ -17,33 +18,43 @@ namespace OpenIddict.Validation.Owin; /// it is NOT recommended to instantiate it as a singleton like a regular OWIN middleware. /// [EditorBrowsable(EditorBrowsableState.Advanced)] -public sealed class OpenIddictValidationOwinMiddleware : AuthenticationMiddleware +public sealed class OpenIddictValidationOwinMiddleware : AuthenticationMiddleware { - private readonly IOpenIddictValidationDispatcher _dispatcher; - private readonly IOpenIddictValidationFactory _factory; + private readonly IServiceProvider _provider; /// /// Creates a new instance of the class. /// /// The next middleware in the pipeline, if applicable. - /// The OpenIddict validation OWIN options. - /// The OpenIddict validation dispatcher. - /// The OpenIddict validation factory. + /// The service provider. public OpenIddictValidationOwinMiddleware( OwinMiddleware? next, - IOptionsMonitor options, - IOpenIddictValidationDispatcher dispatcher, - IOpenIddictValidationFactory factory) - : base(next, options.CurrentValue) - { - _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); - _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - } + IServiceProvider provider) + : base(next, new InternalOptions() + { + AuthenticationMode = provider.GetService>() + ?.CurrentValue.AuthenticationMode + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0169)) + }) + => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); /// /// Creates and returns a new instance. /// /// A new instance of the class. - protected override AuthenticationHandler CreateHandler() - => new OpenIddictValidationOwinHandler(_dispatcher, _factory); + protected override AuthenticationHandler CreateHandler() + => _provider.GetService() + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0169)); + + /// + /// Provides the options used by the class. + /// + private sealed class InternalOptions : AuthenticationOptions + { + /// + /// Creates a new instance of the class. + /// + public InternalOptions() : base(OpenIddictValidationOwinDefaults.AuthenticationType) + => AuthenticationMode = AuthenticationMode.Passive; + } } diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddlewareFactory.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddlewareFactory.cs index a2d1c841..2c29e4a9 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddlewareFactory.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddlewareFactory.cs @@ -5,8 +5,6 @@ */ using System.ComponentModel; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; namespace OpenIddict.Validation.Owin; @@ -39,22 +37,15 @@ public sealed class OpenIddictValidationOwinMiddlewareFactory : OwinMiddleware { ArgumentNullException.ThrowIfNull(context); - var provider = context.Get(typeof(IServiceProvider).FullName) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0168)); - // Note: the Microsoft.Extensions.DependencyInjection container doesn't support resolving services // with arbitrary parameters, which prevents the validation OWIN middleware from being resolved directly // from the DI container, as the next middleware in the pipeline cannot be specified as a parameter. // To work around this limitation, the validation OWIN middleware is manually instantiated and invoked. var middleware = new OpenIddictValidationOwinMiddleware( next: Next, - options: GetRequiredService>(provider), - dispatcher: GetRequiredService(provider), - factory: GetRequiredService(provider)); + provider: context.Get(typeof(IServiceProvider).FullName) + ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0168))); return middleware.Invoke(context); - - static T GetRequiredService(IServiceProvider provider) => provider.GetService() ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0169)); } } diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinOptions.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinOptions.cs index aeed41ce..48981ddb 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinOptions.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinOptions.cs @@ -9,14 +9,15 @@ namespace OpenIddict.Validation.Owin; /// /// Provides various settings needed to configure the OpenIddict OWIN validation integration. /// -public sealed class OpenIddictValidationOwinOptions : AuthenticationOptions +public sealed class OpenIddictValidationOwinOptions { /// - /// Creates a new instance of the class. + /// Gets or sets the authentication mode that will be assigned to the OpenIddict + /// OWIN validation middleware: when using the active mode, the authentication + /// middleware will automatically populate the user identity when the request + /// is processed and will infer a challenge response from HTTP 401 responses. /// - public OpenIddictValidationOwinOptions() - : base(OpenIddictValidationOwinDefaults.AuthenticationType) - => AuthenticationMode = AuthenticationMode.Passive; + public AuthenticationMode AuthenticationMode { get; set; } = AuthenticationMode.Passive; /// /// Gets or sets a boolean indicating whether the built-in logic extracting