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/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));
}
}
}