diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs index 9e2148d9..0536d36e 100644 --- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs +++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs @@ -85,9 +85,19 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(app)); } + var options = app.ApplicationServices.GetRequiredService>().Value; + if (options.Provider == null || options.Provider.GetType() != typeof(OpenIddictServerProvider)) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("OpenIddict can only be used with its built-in server provider.") + .AppendLine("This error may indicate that 'OpenIddictServerOptions.Provider' was manually set.") + .Append("To execute custom request handling logic, consider registering an event handler using ") + .Append("the generic 'services.AddOpenIddict().AddServer().AddEventHandler()' method.") + .ToString()); + } + // When no distributed cache has been registered in the options, use the // global instance registered in the dependency injection container. - var options = app.ApplicationServices.GetRequiredService>().Value; if (options.Cache == null) { options.Cache = app.ApplicationServices.GetRequiredService(); diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs index 7b8f3a29..514c670c 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs @@ -5,6 +5,7 @@ */ using System; +using System.Text; using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; @@ -84,6 +85,16 @@ namespace Microsoft.Extensions.DependencyInjection } var options = app.ApplicationServices.GetRequiredService>().Value; + if (options.Events == null || options.Events.GetType() != typeof(OpenIddictValidationProvider)) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("OpenIddict can only be used with its built-in validation provider.") + .AppendLine("This error may indicate that 'OpenIddictValidationOptions.Events' was manually set.") + .Append("To execute custom request handling logic, consider registering an event handler using ") + .Append("the generic 'services.AddOpenIddict().AddValidation().AddEventHandler()' method.") + .ToString()); + } + if (options.DataProtectionProvider == null) { options.DataProtectionProvider = app.ApplicationServices.GetDataProtectionProvider(); diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs index b8754dd9..6776ba90 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs @@ -1006,7 +1006,7 @@ namespace OpenIddict.Server.Tests new AuthenticationProperties(), OpenIddictServerDefaults.AuthenticationScheme); - ticket.SetProperty(OpenIddictConstants.Properties.InternalTokenId, "3E228451-1555-46F7-A471-951EFBA23A56"); + ticket.SetProperty(OpenIddictConstants.Properties.InternalTokenId, "60FFF7EA-F98E-437B-937E-5073CC313103"); ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken); ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.OfflineAccess); diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs index bb6091f9..9dc33bf0 100644 --- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs +++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs @@ -8,6 +8,7 @@ using System; using System.Reflection; using System.Text; using AspNet.Security.OpenIdConnect.Primitives; +using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder.Internal; using Microsoft.AspNetCore.Http; @@ -106,6 +107,79 @@ namespace OpenIddict.Server.Tests service.ImplementationType == typeof(OpenIddictServerEventService)); } + [Fact] + public void AddServer_CanBeSafelyInvokedMultipleTimes() + { + // Arrange + var services = new ServiceCollection(); + var builder = new OpenIddictBuilder(services); + + // Act and assert + builder.AddServer(); + builder.AddServer(); + builder.AddServer(); + } + + [Fact] + public void UseOpenIddictServer_ThrowsAnExceptionWhenProviderIsNull() + { + // Arrange + var services = new ServiceCollection(); + services.AddOpenIddict() + .AddCore(options => + { + options.SetDefaultApplicationEntity() + .SetDefaultAuthorizationEntity() + .SetDefaultScopeEntity() + .SetDefaultTokenEntity(); + }) + + .AddServer() + .Configure(options => options.Provider = null); + + var builder = new ApplicationBuilder(services.BuildServiceProvider()); + + // Act and assert + var exception = Assert.Throws(() => builder.UseOpenIddictServer()); + + Assert.Equal(new StringBuilder() + .AppendLine("OpenIddict can only be used with its built-in server provider.") + .AppendLine("This error may indicate that 'OpenIddictServerOptions.Provider' was manually set.") + .Append("To execute custom request handling logic, consider registering an event handler using ") + .Append("the generic 'services.AddOpenIddict().AddServer().AddEventHandler()' method.") + .ToString(), exception.Message); + } + + [Fact] + public void UseOpenIddictServer_ThrowsAnExceptionWhenProviderTypeIsIncompatible() + { + // Arrange + var services = new ServiceCollection(); + services.AddOpenIddict() + .AddCore(options => + { + options.SetDefaultApplicationEntity() + .SetDefaultAuthorizationEntity() + .SetDefaultScopeEntity() + .SetDefaultTokenEntity(); + }) + + .AddServer() + .Configure(options => options.Provider = new OpenIdConnectServerProvider()); + + var builder = new ApplicationBuilder(services.BuildServiceProvider()); + + // Act and assert + var exception = Assert.Throws(() => builder.UseOpenIddictServer()); + + Assert.Equal(new StringBuilder() + .AppendLine("OpenIddict can only be used with its built-in server provider.") + .AppendLine("This error may indicate that 'OpenIddictServerOptions.Provider' was manually set.") + .Append("To execute custom request handling logic, consider registering an event handler using ") + .Append("the generic 'services.AddOpenIddict().AddServer().AddEventHandler()' method.") + .ToString(), exception.Message); + } + [Fact] public void UseOpenIddictServer_ThrowsAnExceptionWhenNoFlowIsEnabled() { diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs index 024aed42..bd9425de 100644 --- a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs +++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs @@ -5,6 +5,9 @@ */ using System; +using System.Text; +using AspNet.Security.OAuth.Validation; +using Microsoft.AspNetCore.Builder.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -82,5 +85,62 @@ namespace OpenIddict.Validation.Tests service.ServiceType == typeof(IOpenIddictValidationEventService) && service.ImplementationType == typeof(OpenIddictValidationEventService)); } + + [Fact] + public void AddValidation_CanBeSafelyInvokedMultipleTimes() + { + // Arrange + var services = new ServiceCollection(); + var builder = new OpenIddictBuilder(services); + + // Act and assert + builder.AddValidation(); + builder.AddValidation(); + builder.AddValidation(); + } + + [Fact] + public void UseOpenIddictValidation_ThrowsAnExceptionWhenEventsAreNull() + { + // Arrange + var services = new ServiceCollection(); + services.AddOpenIddict() + .AddValidation() + .Configure(options => options.Events = null); + + var builder = new ApplicationBuilder(services.BuildServiceProvider()); + + // Act and assert + var exception = Assert.Throws(() => builder.UseOpenIddictValidation()); + + Assert.Equal(new StringBuilder() + .AppendLine("OpenIddict can only be used with its built-in validation provider.") + .AppendLine("This error may indicate that 'OpenIddictValidationOptions.Events' was manually set.") + .Append("To execute custom request handling logic, consider registering an event handler using ") + .Append("the generic 'services.AddOpenIddict().AddValidation().AddEventHandler()' method.") + .ToString(), exception.Message); + } + + [Fact] + public void UseOpenIddictValidation_ThrowsAnExceptionWhenEventsTypeIsIncompatible() + { + // Arrange + var services = new ServiceCollection(); + services.AddOpenIddict() + .AddValidation() + .Configure(options => options.Events = new OAuthValidationEvents()); + + var builder = new ApplicationBuilder(services.BuildServiceProvider()); + + // Act and assert + var exception = Assert.Throws(() => builder.UseOpenIddictValidation()); + + Assert.Equal(new StringBuilder() + .AppendLine("OpenIddict can only be used with its built-in validation provider.") + .AppendLine("This error may indicate that 'OpenIddictValidationOptions.Events' was manually set.") + .Append("To execute custom request handling logic, consider registering an event handler using ") + .Append("the generic 'services.AddOpenIddict().AddValidation().AddEventHandler()' method.") + .ToString(), exception.Message); + } } }