diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs b/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs similarity index 87% rename from src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs index 1507cc0b..3464e538 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs @@ -23,17 +23,18 @@ namespace OpenIddict.Server.Internal /// Note: this API supports the OpenIddict infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future minor releases. /// - public class OpenIddictServerInitializer : IPostConfigureOptions + public class OpenIddictServerConfiguration : IConfigureOptions, + IPostConfigureOptions { private readonly IDistributedCache _cache; private readonly IDataProtectionProvider _dataProtectionProvider; /// - /// Creates a new instance of the class. + /// Creates a new instance of the class. /// Note: this API supports the OpenIddict infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future minor releases. /// - public OpenIddictServerInitializer( + public OpenIddictServerConfiguration( [NotNull] IDistributedCache cache, [NotNull] IDataProtectionProvider dataProtectionProvider) { @@ -41,6 +42,29 @@ namespace OpenIddict.Server.Internal _dataProtectionProvider = dataProtectionProvider; } + /// + /// Registers the OpenIddict server handler in the global authentication options. + /// + /// The options instance to initialize. + public void Configure(AuthenticationOptions options) + { + // If a handler was already registered and the type doesn't correspond to the OpenIddict handler, throw an exception. + if (options.SchemeMap.TryGetValue(OpenIddictServerDefaults.AuthenticationScheme, out var builder) && + builder.HandlerType != typeof(OpenIddictServerHandler)) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The OpenIddict server handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OpenID Connect server was registered.") + .Append("Make sure that 'services.AddAuthentication().AddOpenIdConnectServer()' is not used.") + .ToString()); + } + + options.AddScheme(OpenIddictServerDefaults.AuthenticationScheme, scheme => + { + scheme.HandlerType = typeof(OpenIddictServerHandler); + }); + } + /// /// Populates the default OpenID Connect server options and ensure /// that the configuration is in a consistent and valid state. diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs index eeb3ca40..fc29f981 100644 --- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs +++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs @@ -65,37 +65,11 @@ namespace Microsoft.Extensions.DependencyInjection // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. builder.Services.TryAddEnumerable(new[] { - ServiceDescriptor.Singleton, OpenIddictServerInitializer>(), + ServiceDescriptor.Singleton, OpenIddictServerConfiguration>(), + ServiceDescriptor.Singleton, OpenIddictServerConfiguration>(), ServiceDescriptor.Singleton, OpenIdConnectServerInitializer>() }); - // Register the OpenID Connect server handler in the authentication options, - // so it can be discovered by the default authentication handler provider. - builder.Services.Configure(options => - { - // Note: this method is guaranteed to be idempotent. To prevent multiple schemes from being - // registered (which would result in an exception being thrown), a manual check is made here. - if (options.SchemeMap.TryGetValue(OpenIddictServerDefaults.AuthenticationScheme, out var handler)) - { - // If the handler type doesn't correspond to the OpenIddict handler, throw an exception. - if (handler.HandlerType != typeof(OpenIddictServerHandler)) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The OpenIddict server handler cannot be registered as an authentication scheme.") - .AppendLine("This may indicate that an instance of the OpenID Connect server was registered.") - .Append("Make sure that 'services.AddAuthentication().AddOpenIdConnectServer()' is not used.") - .ToString()); - } - - return; - } - - options.AddScheme(OpenIddictServerDefaults.AuthenticationScheme, scheme => - { - scheme.HandlerType = typeof(OpenIddictServerHandler); - }); - }); - return new OpenIddictServerBuilder(builder.Services); } diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs similarity index 65% rename from src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs rename to src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs index ea39812e..46a98afc 100644 --- a/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs +++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs @@ -18,20 +18,45 @@ namespace OpenIddict.Validation.Internal /// Note: this API supports the OpenIddict infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future minor releases. /// - public class OpenIddictValidationInitializer : IPostConfigureOptions + public class OpenIddictValidationConfiguration : IConfigureOptions, + IPostConfigureOptions { private readonly IDataProtectionProvider _dataProtectionProvider; /// - /// Creates a new instance of the class. + /// Creates a new instance of the class. /// Note: this API supports the OpenIddict infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future minor releases. /// - public OpenIddictValidationInitializer([NotNull] IDataProtectionProvider dataProtectionProvider) + public OpenIddictValidationConfiguration([NotNull] IDataProtectionProvider dataProtectionProvider) { _dataProtectionProvider = dataProtectionProvider; } + /// + /// Registers the OpenIddict server handler in the global authentication options. + /// + /// The options instance to initialize. + public void Configure(AuthenticationOptions options) + { + // If a handler was already registered and the type doesn't correspond to the OpenIddict handler, throw an exception. + if (options.SchemeMap.TryGetValue(OpenIddictValidationDefaults.AuthenticationScheme, out var builder) && + builder.HandlerType != typeof(OpenIddictValidationHandler)) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The OpenIddict validation handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OAuth validation or JWT bearer handler was registered.") + .Append("Make sure that neither 'services.AddAuthentication().AddOAuthValidation()' nor ") + .Append("'services.AddAuthentication().AddJwtBearer()' are called from 'ConfigureServices'.") + .ToString()); + } + + options.AddScheme(OpenIddictValidationDefaults.AuthenticationScheme, scheme => + { + scheme.HandlerType = typeof(OpenIddictValidationHandler); + }); + } + /// /// Populates the default OpenIddict validation options and ensure /// that the configuration is in a consistent and valid state. diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs index 202f4a9e..b368eec1 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs @@ -5,7 +5,6 @@ */ using System; -using System.Text; using AspNet.Security.OAuth.Validation; using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; @@ -45,41 +44,15 @@ namespace Microsoft.Extensions.DependencyInjection builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); - // Note: TryAddEnumerable() is used here to ensure the initializer is only registered once. + // Register the options initializers used by the OAuth validation handler and OpenIddict. + // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. builder.Services.TryAddEnumerable(new[] { - ServiceDescriptor.Singleton, OpenIddictValidationInitializer>(), + ServiceDescriptor.Singleton, OpenIddictValidationConfiguration>(), + ServiceDescriptor.Singleton, OpenIddictValidationConfiguration>(), ServiceDescriptor.Singleton, OAuthValidationInitializer>() }); - // Register the OpenIddict validation handler in the authentication options, - // so it can be discovered by the default authentication handler provider. - builder.Services.Configure(options => - { - // Note: this method is guaranteed to be idempotent. To prevent multiple schemes from being - // registered (which would result in an exception being thrown), a manual check is made here. - if (options.SchemeMap.TryGetValue(OpenIddictValidationDefaults.AuthenticationScheme, out var handler)) - { - // If the handler type doesn't correspond to the OpenIddict handler, throw an exception. - if (handler.HandlerType != typeof(OpenIddictValidationHandler)) - { - throw new InvalidOperationException(new StringBuilder() - .AppendLine("The OpenIddict validation handler cannot be registered as an authentication scheme.") - .AppendLine("This may indicate that an instance of the OAuth validation or JWT bearer handler was registered.") - .Append("Make sure that neither 'services.AddAuthentication().AddOAuthValidation()' nor ") - .Append("'services.AddAuthentication().AddJwtBearer()' are called from 'ConfigureServices'.") - .ToString()); - } - - return; - } - - options.AddScheme(OpenIddictValidationDefaults.AuthenticationScheme, scheme => - { - scheme.HandlerType = typeof(OpenIddictValidationHandler); - }); - }); - return new OpenIddictValidationBuilder(builder.Services); } diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs similarity index 92% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs index aceae60e..0ab77543 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs @@ -11,18 +11,45 @@ using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Moq; using OpenIddict.Abstractions; using Xunit; namespace OpenIddict.Server.Internal.Tests { - public class OpenIddictServerInitializerTests + public class OpenIddictServerConfigurationTests { + [Fact] + public void Configure_ThrowsAnExceptionWhenSchemeIsAlreadyRegisteredWithDifferentHandlerType() + { + // Arrange + var options = new AuthenticationOptions(); + options.AddScheme(OpenIddictServerDefaults.AuthenticationScheme, builder => + { + builder.HandlerType = typeof(OpenIdConnectServerHandler); + }); + + var initializer = new OpenIddictServerConfiguration( + Mock.Of(), + Mock.Of()); + + // Act and assert + var exception = Assert.Throws(() => initializer.Configure(options)); + + Assert.Equal(new StringBuilder() + .AppendLine("The OpenIddict server handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OpenID Connect server was registered.") + .Append("Make sure that 'services.AddAuthentication().AddOpenIdConnectServer()' is not used.") + .ToString(), exception.Message); + } + [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenRandomNumberGeneratorIsNull() { diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs index 2924583a..e4ee497f 100644 --- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs +++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs @@ -173,7 +173,7 @@ namespace OpenIddict.Server.Tests } [Theory] - [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictServerInitializer))] + [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictServerConfiguration))] [InlineData(typeof(IPostConfigureOptions), typeof(OpenIdConnectServerInitializer))] public void AddServer_RegistersInitializers(Type serviceType, Type implementationType) { diff --git a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs similarity index 74% rename from test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs rename to test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs index f5b865ff..0f90b2ef 100644 --- a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs +++ b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs @@ -10,16 +10,41 @@ using System.Threading.Tasks; using AspNet.Security.OAuth.Validation; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Moq; using Xunit; namespace OpenIddict.Validation.Internal.Tests { - public class OpenIddictValidationInitializerTests + public class OpenIddictValidationConfigurationTests { + [Fact] + public void Configure_ThrowsAnExceptionWhenSchemeIsAlreadyRegisteredWithDifferentHandlerType() + { + // Arrange + var options = new AuthenticationOptions(); + options.AddScheme(OpenIddictValidationDefaults.AuthenticationScheme, builder => + { + builder.HandlerType = typeof(OAuthValidationHandler); + }); + + var initializer = new OpenIddictValidationConfiguration(Mock.Of()); + + // Act and assert + var exception = Assert.Throws(() => initializer.Configure(options)); + + Assert.Equal(new StringBuilder() + .AppendLine("The OpenIddict validation handler cannot be registered as an authentication scheme.") + .AppendLine("This may indicate that an instance of the OAuth validation or JWT bearer handler was registered.") + .Append("Make sure that neither 'services.AddAuthentication().AddOAuthValidation()' nor ") + .Append("'services.AddAuthentication().AddJwtBearer()' are called from 'ConfigureServices'.") + .ToString(), exception.Message); + } + [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenEventsTypeIsNull() { diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs index d0f92f00..7105b69b 100644 --- a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs +++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs @@ -134,7 +134,7 @@ namespace OpenIddict.Validation.Tests } [Theory] - [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictValidationInitializer))] + [InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictValidationConfiguration))] [InlineData(typeof(IPostConfigureOptions), typeof(OAuthValidationInitializer))] public void AddValidation_RegistersInitializers(Type serviceType, Type implementationType) {