/* * 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.ComponentModel; using Microsoft.Extensions.Options; namespace OpenIddict.Server.AspNetCore; /// /// Contains the methods required to ensure that the OpenIddict server configuration is valid. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictServerAspNetCoreConfiguration : IConfigureOptions, IConfigureOptions, IPostConfigureOptions, IPostConfigureOptions, IPostConfigureOptions { /// public void Configure(AuthenticationOptions options) { ArgumentNullException.ThrowIfNull(options); // If a handler was already registered and the type doesn't correspond to the OpenIddict handler, throw an exception. if (options.SchemeMap.TryGetValue(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, out var builder) && builder.HandlerType != typeof(OpenIddictServerAspNetCoreHandler)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0108)); } options.AddScheme( OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, displayName: null); } /// public void Configure(OpenIddictServerOptions options) { ArgumentNullException.ThrowIfNull(options); // Register the built-in event handlers used by the OpenIddict ASP.NET Core server components. options.Handlers.AddRange(OpenIddictServerAspNetCoreHandlers.DefaultHandlers); // Enable client_secret_basic support by default. options.ClientAuthenticationMethods.Add(ClientAuthenticationMethods.ClientSecretBasic); } /// public void PostConfigure(string? name, AuthenticationOptions options) { ArgumentNullException.ThrowIfNull(options); if (!TryValidate(options.SchemeMap, options.DefaultAuthenticateScheme) || !TryValidate(options.SchemeMap, options.DefaultChallengeScheme) || !TryValidate(options.SchemeMap, options.DefaultForbidScheme) || !TryValidate(options.SchemeMap, options.DefaultScheme) || !TryValidate(options.SchemeMap, options.DefaultSignInScheme) || !TryValidate(options.SchemeMap, options.DefaultSignOutScheme)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0109)); } #if SUPPORTS_AUTHENTICATION_HANDLER_SELECTION_FALLBACK // Starting in ASP.NET 7.0, the authentication stack integrates a fallback // mechanism to select the default scheme to use when no value is set, but // only if a single handler has been registered in the authentication options. // // Unfortunately, this behavior is problematic for OpenIddict as it enforces // strict checks to prevent calling certain unsafe authentication operations // on invalid endpoints. To opt out this undesirable behavior, a fake entry // is dynamically added if one of the default schemes properties is not set // and less than 2 handlers were registered in the authentication options. if (options.SchemeMap.Count is < 2 && string.IsNullOrEmpty(options.DefaultScheme) && (string.IsNullOrEmpty(options.DefaultAuthenticateScheme) || string.IsNullOrEmpty(options.DefaultChallengeScheme) || string.IsNullOrEmpty(options.DefaultForbidScheme) || string.IsNullOrEmpty(options.DefaultSignInScheme) || string.IsNullOrEmpty(options.DefaultSignOutScheme))) { options.AddScheme(Guid.NewGuid().ToString(), displayName: null); } #endif static bool TryValidate(IDictionary map, string? scheme) { // If the scheme was not set or if it cannot be found in the map, return true. if (string.IsNullOrEmpty(scheme) || !map.TryGetValue(scheme, out var builder)) { return true; } return builder.HandlerType != typeof(OpenIddictServerAspNetCoreHandler); } } /// public void PostConfigure(string? name, OpenIddictServerAspNetCoreOptions options) { ArgumentNullException.ThrowIfNull(options); if (options.EnableErrorPassthrough && options.EnableStatusCodePagesIntegration) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0110)); } } /// public void PostConfigure(string? name, OpenIddictServerOptions options) { ArgumentNullException.ThrowIfNull(options); // Enable tls_client_auth and self_signed_tls_client_auth support if the // corresponding chain policies have been configured in the server options. if (options.ClientCertificateChainPolicy is not null) { options.ClientAuthenticationMethods.Add(ClientAuthenticationMethods.TlsClientAuth); } if (options.SelfSignedClientCertificateChainPolicy is not null) { options.ClientAuthenticationMethods.Add(ClientAuthenticationMethods.SelfSignedTlsClientAuth); } } }