diff --git a/src/OpenIddict.Mvc/Internal/OpenIddictMvcConfiguration.cs b/src/OpenIddict.Mvc/Internal/OpenIddictMvcConfiguration.cs
new file mode 100644
index 00000000..23b1ba74
--- /dev/null
+++ b/src/OpenIddict.Mvc/Internal/OpenIddictMvcConfiguration.cs
@@ -0,0 +1,39 @@
+/*
+ * 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 System;
+using AspNet.Security.OpenIdConnect.Primitives;
+using JetBrains.Annotations;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.Extensions.Options;
+
+namespace OpenIddict.Mvc.Internal
+{
+ ///
+ /// Contains the methods required to ensure that the OpenIddict MVC configuration is valid.
+ /// 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 OpenIddictMvcConfiguration : IConfigureOptions
+ {
+ ///
+ /// Registers the OpenIddict MVC components in the MVC options.
+ ///
+ /// The options instance to initialize.
+ public void Configure([NotNull] MvcOptions options)
+ {
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ options.ModelBinderProviders.Insert(0, new OpenIddictMvcBinderProvider());
+ options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(OpenIdConnectRequest)));
+ options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(OpenIdConnectResponse)));
+ }
+ }
+}
diff --git a/src/OpenIddict.Mvc/OpenIddictMvcExtensions.cs b/src/OpenIddict.Mvc/OpenIddictMvcExtensions.cs
index 19320f1b..ff22394a 100644
--- a/src/OpenIddict.Mvc/OpenIddictMvcExtensions.cs
+++ b/src/OpenIddict.Mvc/OpenIddictMvcExtensions.cs
@@ -7,6 +7,8 @@
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
using OpenIddict.Mvc.Internal;
namespace Microsoft.Extensions.DependencyInjection
@@ -29,20 +31,9 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(builder));
}
- builder.Services.Configure(options =>
- {
- // Skip the binder registration if it was already added to the providers collection.
- for (var index = 0; index < options.ModelBinderProviders.Count; index++)
- {
- var provider = options.ModelBinderProviders[index];
- if (provider is OpenIddictMvcBinderProvider)
- {
- return;
- }
- }
-
- options.ModelBinderProviders.Insert(0, new OpenIddictMvcBinderProvider());
- });
+ // Register the options initializer used by the OpenIddict MVC binding/validation components.
+ // Note: TryAddEnumerable() is used here to ensure the initializer is only registered once.
+ builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, OpenIddictMvcConfiguration>());
return new OpenIddictMvcBuilder(builder.Services);
}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs
index e449a036..397ee637 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerConfiguration.cs
@@ -47,8 +47,13 @@ namespace OpenIddict.Server.Internal
/// Registers the OpenIddict server handler in the global authentication options.
///
/// The options instance to initialize.
- public void Configure(AuthenticationOptions options)
+ public void Configure([NotNull] AuthenticationOptions options)
{
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(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))
diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs
index 022d2e7b..fc9e4e44 100644
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs
+++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationConfiguration.cs
@@ -36,8 +36,13 @@ namespace OpenIddict.Validation.Internal
/// Registers the OpenIddict validation handler in the global authentication options.
///
/// The options instance to initialize.
- public void Configure(AuthenticationOptions options)
+ public void Configure([NotNull] AuthenticationOptions options)
{
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(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))
diff --git a/test/OpenIddict.Mvc.Tests/Internal/OpenIddictMvcConfigurationTests.cs b/test/OpenIddict.Mvc.Tests/Internal/OpenIddictMvcConfigurationTests.cs
new file mode 100644
index 00000000..8b76a50b
--- /dev/null
+++ b/test/OpenIddict.Mvc.Tests/Internal/OpenIddictMvcConfigurationTests.cs
@@ -0,0 +1,71 @@
+/*
+ * 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 System;
+using System.Linq;
+using AspNet.Security.OpenIdConnect.Primitives;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using OpenIddict.Mvc.Internal;
+using Xunit;
+
+namespace OpenIddict.Mvc.Tests
+{
+ public class OpenIddictConfigurationExtensionsTests
+ {
+ [Fact]
+ public void Configure_ThrowsAnExceptionForNullOptions()
+ {
+ // Arrange
+ var configuration = new OpenIddictMvcConfiguration();
+
+ // Act and assert
+ var exception = Assert.Throws(() => configuration.Configure(null));
+
+ Assert.Equal("options", exception.ParamName);
+ }
+
+ [Fact]
+ public void Configure_RegistersModelBinderProvider()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ services.AddOptions();
+
+ var builder = new OpenIddictServerBuilder(services);
+
+ // Act
+ builder.UseMvc();
+
+ var options = services.BuildServiceProvider().GetRequiredService>();
+
+ // Assert
+ Assert.Contains(options.Value.ModelBinderProviders, binder => binder is OpenIddictMvcBinderProvider);
+ }
+
+ [Fact]
+ public void Configure_RegistersModelMetadataDetailsProviders()
+ {
+ // Arrange
+ var services = new ServiceCollection();
+ services.AddOptions();
+
+ var builder = new OpenIddictServerBuilder(services);
+
+ // Act
+ builder.UseMvc();
+
+ var options = services.BuildServiceProvider().GetRequiredService>();
+
+ // Assert
+ var providers = options.Value.ModelMetadataDetailsProviders.OfType();
+ Assert.Contains(providers, provider => provider.Type == typeof(OpenIdConnectRequest));
+ Assert.Contains(providers, provider => provider.Type == typeof(OpenIdConnectResponse));
+ }
+ }
+}
diff --git a/test/OpenIddict.Mvc.Tests/OpenIddictMvcExtensionsTests.cs b/test/OpenIddict.Mvc.Tests/OpenIddictMvcExtensionsTests.cs
index 027eeec6..6421d311 100644
--- a/test/OpenIddict.Mvc.Tests/OpenIddictMvcExtensionsTests.cs
+++ b/test/OpenIddict.Mvc.Tests/OpenIddictMvcExtensionsTests.cs
@@ -41,7 +41,7 @@ namespace OpenIddict.Mvc.Tests
}
[Fact]
- public void UseMvc_RegistersModelBinderProvider()
+ public void UseMvc_RegistersConfiguration()
{
// Arrange
var services = new ServiceCollection();
@@ -52,11 +52,9 @@ namespace OpenIddict.Mvc.Tests
// Act
builder.UseMvc();
- var provider = services.BuildServiceProvider();
- var options = provider.GetRequiredService>();
-
// Assert
- Assert.Contains(options.Value.ModelBinderProviders, binder => binder is OpenIddictMvcBinderProvider);
+ Assert.Contains(services, service => service.ServiceType == typeof(IConfigureOptions) &&
+ service.ImplementationType == typeof(OpenIddictMvcConfiguration));
}
}
}
diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs
index c3896d3a..b5d93355 100644
--- a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs
+++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerConfigurationTests.cs
@@ -27,6 +27,20 @@ namespace OpenIddict.Server.Internal.Tests
{
public class OpenIddictServerConfigurationTests
{
+ [Fact]
+ public void Configure_ThrowsAnExceptionForNullOptions()
+ {
+ // Arrange
+ var configuration = new OpenIddictServerConfiguration(
+ Mock.Of(),
+ Mock.Of());
+
+ // Act and assert
+ var exception = Assert.Throws(() => configuration.Configure(null));
+
+ Assert.Equal("options", exception.ParamName);
+ }
+
[Fact]
public void Configure_ThrowsAnExceptionWhenSchemeIsAlreadyRegisteredWithDifferentHandlerType()
{
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
index dd0646c4..e890a351 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
@@ -175,7 +175,7 @@ namespace OpenIddict.Server.Tests
[Theory]
[InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictServerConfiguration))]
[InlineData(typeof(IPostConfigureOptions), typeof(OpenIdConnectServerInitializer))]
- public void AddServer_RegistersInitializers(Type serviceType, Type implementationType)
+ public void AddServer_RegistersConfiguration(Type serviceType, Type implementationType)
{
// Arrange
var services = new ServiceCollection();
diff --git a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs
index 755f00a0..3b86357b 100644
--- a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs
+++ b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationConfigurationTests.cs
@@ -23,6 +23,18 @@ namespace OpenIddict.Validation.Internal.Tests
{
public class OpenIddictValidationConfigurationTests
{
+ [Fact]
+ public void Configure_ThrowsAnExceptionForNullOptions()
+ {
+ // Arrange
+ var configuration = new OpenIddictValidationConfiguration(Mock.Of());
+
+ // Act and assert
+ var exception = Assert.Throws(() => configuration.Configure(null));
+
+ Assert.Equal("options", exception.ParamName);
+ }
+
[Theory]
[InlineData(new object[] { new string[] { OpenIddictValidationDefaults.AuthenticationScheme, null } })]
[InlineData(new object[] { new string[] { null, OpenIddictValidationDefaults.AuthenticationScheme } })]
diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
index 7af9b1b7..99be6172 100644
--- a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
@@ -136,7 +136,7 @@ namespace OpenIddict.Validation.Tests
[Theory]
[InlineData(typeof(IPostConfigureOptions), typeof(OpenIddictValidationConfiguration))]
[InlineData(typeof(IPostConfigureOptions), typeof(OAuthValidationInitializer))]
- public void AddValidation_RegistersInitializers(Type serviceType, Type implementationType)
+ public void AddValidation_RegistersConfiguration(Type serviceType, Type implementationType)
{
// Arrange
var services = new ServiceCollection();