From 2a1e47624b7a0db068e1134a3ed536bba70cfef0 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 1 Jun 2022 14:44:13 -0700 Subject: [PATCH 1/5] Add support for reading default scheme from config --- .../WebApplicationAuthenticationBuilder.cs | 12 ++-- .../IAuthenticationConfigurationProvider.cs | 7 +++ .../src/PublicAPI.Unshipped.txt | 1 + .../Core/src/AuthenticationBuilder.cs | 6 +- .../AuthenticationConfigurationConstants.cs | 24 ++++++++ .../src/AuthenticationConfigureOptions.cs | 26 ++++++++ ...aultAuthenticationConfigurationProvider.cs | 7 ++- .../Core/src/PublicAPI.Unshipped.txt | 4 ++ .../samples/MinimalJwtBearerSample/Program.cs | 1 + .../appsettings.Development.json | 10 +++- .../test/AuthenticationMiddlewareTests.cs | 2 + .../Authentication/test/CertificateTests.cs | 2 + .../Authentication/test/JwtBearerTests.cs | 37 ++++++++++++ .../OpenIdConnectConfigurationTests.cs | 9 +++ .../Authentication/test/PolicyTests.cs | 6 ++ .../test/SharedAuthenticationTests.cs | 59 +++++++++++++++++++ .../test/WsFederation/WsFederationTest.cs | 2 + 17 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs create mode 100644 src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs diff --git a/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs b/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs index f95ca8a05896..f53de4ea8f25 100644 --- a/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs @@ -14,34 +14,34 @@ public WebApplicationAuthenticationBuilder(IServiceCollection services) : base(s public override AuthenticationBuilder AddPolicyScheme(string authenticationScheme, string? displayName, Action configureOptions) { - RegisterServices(authenticationScheme); + RegisterServices(); return base.AddPolicyScheme(authenticationScheme, displayName, configureOptions); } public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string? displayName, Action? configureOptions) { - RegisterServices(authenticationScheme); + RegisterServices(); return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions); } public override AuthenticationBuilder AddScheme(string authenticationScheme, string? displayName, Action? configureOptions) { - RegisterServices(authenticationScheme); + RegisterServices(); return base.AddScheme(authenticationScheme, displayName, configureOptions); } public override AuthenticationBuilder AddScheme(string authenticationScheme, Action? configureOptions) { - RegisterServices(authenticationScheme); + RegisterServices(); return base.AddScheme(authenticationScheme, configureOptions); } - private void RegisterServices(string authenticationScheme) + private void RegisterServices() { if (!IsAuthenticationConfigured) { IsAuthenticationConfigured = true; - Services.AddAuthentication(authenticationScheme); + Services.AddAuthentication(); Services.AddAuthorization(); } } diff --git a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs index 0665de7acb0b..cd21508b9700 100644 --- a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs +++ b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs @@ -17,4 +17,11 @@ public interface IAuthenticationConfigurationProvider /// The path to the section to be returned. /// The specified object, or null if the requested section does not exist. IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme); + + /// + /// Returns the where authentication + /// options are stored. + /// + /// The specified object, or null if the requested section does not exist. + IConfiguration GetAuthenticationConfiguration(); } diff --git a/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt index efe32d90a931..5c70bd0c6393 100644 --- a/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ #nullable enable Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider +Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider.GetAuthenticationConfiguration() -> Microsoft.Extensions.Configuration.IConfiguration! Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider.GetAuthenticationSchemeConfiguration(string! authenticationScheme) -> Microsoft.Extensions.Configuration.IConfiguration! diff --git a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs index 9a2c7deea1fe..face6057be59 100644 --- a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs +++ b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs @@ -18,7 +18,11 @@ public class AuthenticationBuilder /// /// The services being configured. public AuthenticationBuilder(IServiceCollection services) - => Services = services; + { + Services = services; + // Always try to read global authentication options from configuration + Services.TryAddEnumerable(ServiceDescriptor.Singleton, AuthenticationConfigureOptions>()); + } /// /// The services being configured. diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs b/src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs new file mode 100644 index 000000000000..65e2cea8627f --- /dev/null +++ b/src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Authentication; + +/// +/// Constants for managing generating authentication options from +/// configuration. +/// +public static class AuthenticationConfigurationConstants +{ + /// + /// The top-level "Authentication" key. + /// + public const string Authentication = "Authentication"; + /// + /// The "Schemes" key for registering schemes. + /// + public const string Schemes = "Schemes"; + /// + /// The "DefaultScheme" key. + /// + public const string DefaultScheme = "DefaultScheme"; +} diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs new file mode 100644 index 000000000000..bcb1dcf1683c --- /dev/null +++ b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication; + +internal sealed class AuthenticationConfigureOptions : IConfigureOptions +{ + private readonly IAuthenticationConfigurationProvider _authenticationConfigurationProvider; + + public AuthenticationConfigureOptions(IAuthenticationConfigurationProvider configurationProvider) + { + _authenticationConfigurationProvider = configurationProvider; + } + + public void Configure(AuthenticationOptions options) + { + var authenticationConfig = _authenticationConfigurationProvider.GetAuthenticationConfiguration(); + var defaultScheme = authenticationConfig[AuthenticationConfigurationConstants.DefaultScheme]; + if (defaultScheme is not null) + { + options.DefaultScheme = defaultScheme; + } + } +} diff --git a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs index 057e1a5ad1d4..f377fb77a68e 100644 --- a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs +++ b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs @@ -14,8 +14,13 @@ public DefaultAuthenticationConfigurationProvider(IConfiguration configuration) _configuration = configuration; } + public IConfiguration GetAuthenticationConfiguration() + { + return _configuration.GetSection(AuthenticationConfigurationConstants.Authentication); + } + public IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme) { - return _configuration.GetSection($"Authentication:Schemes:{authenticationScheme}"); + return _configuration.GetSection($"{AuthenticationConfigurationConstants.Authentication}:{AuthenticationConfigurationConstants.Schemes}:{authenticationScheme}"); } } diff --git a/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt b/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..190a62f1bc52 100644 --- a/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ #nullable enable +Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants +const Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants.Authentication = "Authentication" -> string! +const Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants.DefaultScheme = "DefaultScheme" -> string! +const Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants.Schemes = "Schemes" -> string! diff --git a/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs b/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs index ff0c3ecd22cb..470b722d6ef6 100644 --- a/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs +++ b/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs @@ -10,6 +10,7 @@ builder.Authentication.AddJwtBearer(); builder.Authentication.AddJwtBearer("ClaimedDetails"); +builder.Authentication.AddJwtBearer("InvalidScheme"); builder.Services.AddAuthorization(options => options.AddPolicy("is_admin", policy => diff --git a/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/appsettings.Development.json b/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/appsettings.Development.json index 9fe2b7a74f48..6faede201cac 100644 --- a/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/appsettings.Development.json +++ b/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/appsettings.Development.json @@ -6,6 +6,7 @@ } }, "Authentication": { + "DefaultScheme": "ClaimedDetails", "Schemes": { "Bearer": { "Audiences": [ @@ -20,7 +21,14 @@ "http://localhost:5259" ], "ClaimsIssuer": "dotnet-user-jwts" + }, + "InvalidScheme": { + "Audiences": [ + "https://localhost:7259", + "http://localhost:5259" + ], + "ClaimsIssuer": "invalid-issuer" } } } -} \ No newline at end of file +} diff --git a/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs b/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs index eb883f111d7a..422a4e633f43 100644 --- a/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs +++ b/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -183,6 +184,7 @@ private HttpContext GetHttpContext( serviceCollection.AddOptions(); serviceCollection.AddLogging(); serviceCollection.AddAuthentication(); + serviceCollection.AddSingleton(new ConfigurationManager()); registerServices?.Invoke(serviceCollection); var serviceProvider = serviceCollection.BuildServiceProvider(); diff --git a/src/Security/Authentication/test/CertificateTests.cs b/src/Security/Authentication/test/CertificateTests.cs index 018ccb139171..5b50f7ee47ca 100644 --- a/src/Security/Authentication/test/CertificateTests.cs +++ b/src/Security/Authentication/test/CertificateTests.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -25,6 +26,7 @@ public class ClientCertificateAuthenticationTests public async Task VerifySchemeDefaults() { var services = new ServiceCollection(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication().AddCertificate(); var sp = services.BuildServiceProvider(); var schemeProvider = sp.GetRequiredService(); diff --git a/src/Security/Authentication/test/JwtBearerTests.cs b/src/Security/Authentication/test/JwtBearerTests.cs index f1dcb84eaa73..936e137f2ec6 100755 --- a/src/Security/Authentication/test/JwtBearerTests.cs +++ b/src/Security/Authentication/test/JwtBearerTests.cs @@ -9,10 +9,12 @@ using System.Text; using System.Text.Json; using System.Xml.Linq; +using Microsoft.AspNetCore.Authentication.Tests; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; @@ -883,6 +885,41 @@ public async Task ExpirationAndIssuedNullWhenMinOrMaxValue() Assert.Equal(JsonValueKind.Null, dom.RootElement.GetProperty("issued").ValueKind); } + [Fact] + public async Task ForwardSchemeOverridesSchemeFromConfig() + { + // Arrange + var defaultSchemeFromConfig = "DefaultSchemeFromConfig"; + var defaultSchemeFromForward = "DefaultSchemeFromForward"; + var services = new ServiceCollection().AddLogging(); + var config = new ConfigurationBuilder().AddInMemoryCollection(new[] + { + new KeyValuePair("Authentication:DefaultScheme", defaultSchemeFromConfig) + }).Build(); + services.AddSingleton(config); + + // Act + var builder = services.AddAuthentication(o => + { + o.AddScheme(defaultSchemeFromForward, defaultSchemeFromForward); + }); + builder.AddJwtBearer(defaultSchemeFromConfig, o => o.ForwardAuthenticate = defaultSchemeFromForward); + var forwardAuthentication = new TestHandler(); + services.AddSingleton(forwardAuthentication); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + // Assert + Assert.Equal(0, forwardAuthentication.AuthenticateCount); + await context.AuthenticateAsync(); + Assert.Equal(1, forwardAuthentication.AuthenticateCount); + var schemeProvider = sp.GetRequiredService(); + var defaultSchemeFromServices = await schemeProvider.GetDefaultAuthenticateSchemeAsync(); + Assert.Equal(defaultSchemeFromConfig, defaultSchemeFromServices.Name); + } + class InvalidTokenValidator : ISecurityTokenValidator { public InvalidTokenValidator() diff --git a/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs b/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs index 2e854c0ba4f0..02bc067cda8a 100644 --- a/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs +++ b/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -28,6 +29,7 @@ private void ConfigureDefaults(OpenIdConnectOptions o) public async Task CanForwardDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { @@ -102,6 +104,7 @@ public async Task ForwardSignInThrows() public async Task ForwardSignOutWinsOverDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { @@ -143,6 +146,7 @@ public async Task ForwardSignOutWinsOverDefault() public async Task ForwardForbidWinsOverDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { @@ -184,6 +188,7 @@ public async Task ForwardForbidWinsOverDefault() public async Task ForwardAuthenticateWinsOverDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { @@ -225,6 +230,7 @@ public async Task ForwardAuthenticateWinsOverDefault() public async Task ForwardChallengeWinsOverDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -265,6 +271,7 @@ public async Task ForwardChallengeWinsOverDefault() public async Task ForwardSelectorWinsOverDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -320,6 +327,7 @@ public async Task ForwardSelectorWinsOverDefault() public async Task NullForwardSelectorUsesDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -375,6 +383,7 @@ public async Task NullForwardSelectorUsesDefault() public async Task SpecificForwardWinsOverSelectorAndDefault() { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; diff --git a/src/Security/Authentication/test/PolicyTests.cs b/src/Security/Authentication/test/PolicyTests.cs index 79030893499f..2d7c22533e26 100644 --- a/src/Security/Authentication/test/PolicyTests.cs +++ b/src/Security/Authentication/test/PolicyTests.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -54,6 +55,7 @@ public async Task CanDispatch() public async Task DefaultTargetSelectorWinsOverDefaultTarget() { var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -110,6 +112,7 @@ public async Task DefaultTargetSelectorWinsOverDefaultTarget() public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() { var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -166,6 +169,7 @@ public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() public async Task SpecificTargetAlwaysWinsOverDefaultTarget() { var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -227,6 +231,7 @@ public async Task SpecificTargetAlwaysWinsOverDefaultTarget() public async Task VirtualSchemeTargetsForwardWithDefaultTarget() { var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -279,6 +284,7 @@ public async Task VirtualSchemeTargetsForwardWithDefaultTarget() public async Task VirtualSchemeTargetsOverrideDefaultTarget() { var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); diff --git a/src/Security/Authentication/test/SharedAuthenticationTests.cs b/src/Security/Authentication/test/SharedAuthenticationTests.cs index f1085b604169..395f021879d1 100644 --- a/src/Security/Authentication/test/SharedAuthenticationTests.cs +++ b/src/Security/Authentication/test/SharedAuthenticationTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Security.Claims; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.Tests; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; @@ -84,6 +85,7 @@ public async Task ForwardSignInWinsOverDefault() if (SupportsSignIn) { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); var builder = services.AddAuthentication(o => { @@ -127,6 +129,7 @@ public async Task ForwardSignOutWinsOverDefault() if (SupportsSignOut) { var services = new ServiceCollection().AddLogging(); + services.AddSingleton(new ConfigurationManager()); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -543,6 +546,7 @@ public async Task SpecificForwardWinsOverSelectorAndDefault() public async Task VerifySchemeDefaults() { var services = new ServiceCollection(); + services.AddSingleton(new ConfigurationManager()); var builder = services.AddAuthentication(); RegisterAuth(builder, o => { }); var sp = services.BuildServiceProvider(); @@ -552,4 +556,59 @@ public async Task VerifySchemeDefaults() Assert.Equal(HandlerType, scheme.HandlerType); Assert.Equal(DisplayName, scheme.DisplayName); } + + [Fact] + public async Task RespectsDefaultSchemeInConfig() + { + // Arrange + var defaultSchemeFromConfig = "DefaultSchemeFromConfig"; + var services = new ServiceCollection(); + var config = new ConfigurationBuilder().AddInMemoryCollection(new[] + { + new KeyValuePair("Authentication:DefaultScheme", defaultSchemeFromConfig) + }).Build(); + services.AddSingleton(config); + + // Act + var builder = services.AddAuthentication(o => + { + o.AddScheme(defaultSchemeFromConfig, defaultSchemeFromConfig); + }); + RegisterAuth(builder, _ => { }); + var sp = services.BuildServiceProvider(); + + // Assert + var schemeProvider = sp.GetRequiredService(); + var defaultSchemeFromServices = await schemeProvider.GetDefaultAuthenticateSchemeAsync(); + Assert.Equal(defaultSchemeFromConfig, defaultSchemeFromServices.Name); + } + + [Fact] + public async Task CanOverrideDefaultInConfigViaAddAuthentication() + { + // Arrange + var defaultSchemeFromConfig = "DefaultSchemeFromConfig"; + var defaultSchemeFromAddAuth = "DefaultSchemeFromAddAuth"; + var services = new ServiceCollection(); + var config = new ConfigurationBuilder().AddInMemoryCollection(new[] + { + new KeyValuePair("Authentication:DefaultScheme", defaultSchemeFromConfig) + }).Build(); + services.AddSingleton(config); + + // Act + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = defaultSchemeFromAddAuth; + o.AddScheme(defaultSchemeFromConfig, defaultSchemeFromConfig); + o.AddScheme(defaultSchemeFromAddAuth, defaultSchemeFromAddAuth); + }); + RegisterAuth(builder, _ => { }); + var sp = services.BuildServiceProvider(); + + // Assert + var schemeProvider = sp.GetRequiredService(); + var defaultSchemeFromServices = await schemeProvider.GetDefaultAuthenticateSchemeAsync(); + Assert.Equal(defaultSchemeFromAddAuth, defaultSchemeFromServices.Name); + } } diff --git a/src/Security/Authentication/test/WsFederation/WsFederationTest.cs b/src/Security/Authentication/test/WsFederation/WsFederationTest.cs index d28490a162ad..bdbc4ad06a9d 100644 --- a/src/Security/Authentication/test/WsFederation/WsFederationTest.cs +++ b/src/Security/Authentication/test/WsFederation/WsFederationTest.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; @@ -25,6 +26,7 @@ public class WsFederationTest public async Task VerifySchemeDefaults() { var services = new ServiceCollection(); + services.AddSingleton(new ConfigurationManager()); services.AddAuthentication().AddWsFederation(); var sp = services.BuildServiceProvider(); var schemeProvider = sp.GetRequiredService(); From f3d5d7c4245a50dca4c899da851b2728f74655d5 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Sun, 5 Jun 2022 19:07:41 -0700 Subject: [PATCH 2/5] Address feedback and update API --- .../IAuthenticationConfigurationProvider.cs | 4 +-- .../AuthenticationConfigurationConstants.cs | 24 -------------- .../src/AuthenticationConfigureOptions.cs | 5 +-- ...aultAuthenticationConfigurationProvider.cs | 6 ++-- .../Core/src/PublicAPI.Unshipped.txt | 4 --- .../Authentication/test/CertificateTests.cs | 3 +- .../OpenIdConnectConfigurationTests.cs | 31 +++++------------ .../Authentication/test/PolicyTests.cs | 15 +++------ .../test/SharedAuthenticationTests.cs | 33 +++++++------------ .../Authentication/test/TestExtensions.cs | 10 ++++++ 10 files changed, 45 insertions(+), 90 deletions(-) delete mode 100644 src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs diff --git a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs index cd21508b9700..895da5d5ad07 100644 --- a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs +++ b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs @@ -15,13 +15,13 @@ public interface IAuthenticationConfigurationProvider /// Returns the specified object. /// /// The path to the section to be returned. - /// The specified object, or null if the requested section does not exist. + /// The specified object, or null if the requested section does not exist. IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme); /// /// Returns the where authentication /// options are stored. /// - /// The specified object, or null if the requested section does not exist. + /// The specified object, or null if the requested section does not exist. IConfiguration GetAuthenticationConfiguration(); } diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs b/src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs deleted file mode 100644 index 65e2cea8627f..000000000000 --- a/src/Security/Authentication/Core/src/AuthenticationConfigurationConstants.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Authentication; - -/// -/// Constants for managing generating authentication options from -/// configuration. -/// -public static class AuthenticationConfigurationConstants -{ - /// - /// The top-level "Authentication" key. - /// - public const string Authentication = "Authentication"; - /// - /// The "Schemes" key for registering schemes. - /// - public const string Schemes = "Schemes"; - /// - /// The "DefaultScheme" key. - /// - public const string DefaultScheme = "DefaultScheme"; -} diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs index bcb1dcf1683c..3d34992a1a33 100644 --- a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs +++ b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs @@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Authentication; internal sealed class AuthenticationConfigureOptions : IConfigureOptions { private readonly IAuthenticationConfigurationProvider _authenticationConfigurationProvider; + private const string DefaultSchemeKey = "DefaultScheme"; public AuthenticationConfigureOptions(IAuthenticationConfigurationProvider configurationProvider) { @@ -17,8 +18,8 @@ public AuthenticationConfigureOptions(IAuthenticationConfigurationProvider confi public void Configure(AuthenticationOptions options) { var authenticationConfig = _authenticationConfigurationProvider.GetAuthenticationConfiguration(); - var defaultScheme = authenticationConfig[AuthenticationConfigurationConstants.DefaultScheme]; - if (defaultScheme is not null) + var defaultScheme = authenticationConfig[DefaultSchemeKey]; + if (!string.IsNullOrEmpty(defaultScheme)) { options.DefaultScheme = defaultScheme; } diff --git a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs index f377fb77a68e..98840286a8a1 100644 --- a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs +++ b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs @@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Authentication; internal sealed class DefaultAuthenticationConfigurationProvider : IAuthenticationConfigurationProvider { private readonly IConfiguration _configuration; + private const string AuthenticationKey = "Authentication"; + private const string AuthenticationSchemesKey = "Authentication:Schemes"; public DefaultAuthenticationConfigurationProvider(IConfiguration configuration) { @@ -16,11 +18,11 @@ public DefaultAuthenticationConfigurationProvider(IConfiguration configuration) public IConfiguration GetAuthenticationConfiguration() { - return _configuration.GetSection(AuthenticationConfigurationConstants.Authentication); + return _configuration.GetSection(AuthenticationKey); } public IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme) { - return _configuration.GetSection($"{AuthenticationConfigurationConstants.Authentication}:{AuthenticationConfigurationConstants.Schemes}:{authenticationScheme}"); + return _configuration.GetSection($"{AuthenticationSchemesKey}:{authenticationScheme}"); } } diff --git a/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt b/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt index 190a62f1bc52..7dc5c58110bf 100644 --- a/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt @@ -1,5 +1 @@ #nullable enable -Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants -const Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants.Authentication = "Authentication" -> string! -const Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants.DefaultScheme = "DefaultScheme" -> string! -const Microsoft.AspNetCore.Authentication.AuthenticationConfigurationConstants.Schemes = "Schemes" -> string! diff --git a/src/Security/Authentication/test/CertificateTests.cs b/src/Security/Authentication/test/CertificateTests.cs index 5b50f7ee47ca..823f5c50e8ae 100644 --- a/src/Security/Authentication/test/CertificateTests.cs +++ b/src/Security/Authentication/test/CertificateTests.cs @@ -25,8 +25,7 @@ public class ClientCertificateAuthenticationTests [Fact] public async Task VerifySchemeDefaults() { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication().AddCertificate(); var sp = services.BuildServiceProvider(); var schemeProvider = sp.GetRequiredService(); diff --git a/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs b/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs index 02bc067cda8a..95be59c52e2f 100644 --- a/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs +++ b/src/Security/Authentication/test/OpenIdConnect/OpenIdConnectConfigurationTests.cs @@ -28,9 +28,7 @@ private void ConfigureDefaults(OpenIdConnectOptions o) [Fact] public async Task CanForwardDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -73,8 +71,7 @@ public async Task CanForwardDefault() [Fact] public async Task ForwardSignInThrows() { - var services = new ServiceCollection().AddLogging(); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -103,9 +100,7 @@ public async Task ForwardSignInThrows() [Fact] public async Task ForwardSignOutWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -145,9 +140,7 @@ public async Task ForwardSignOutWinsOverDefault() [Fact] public async Task ForwardForbidWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -187,9 +180,7 @@ public async Task ForwardForbidWinsOverDefault() [Fact] public async Task ForwardAuthenticateWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -229,8 +220,7 @@ public async Task ForwardAuthenticateWinsOverDefault() [Fact] public async Task ForwardChallengeWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -270,8 +260,7 @@ public async Task ForwardChallengeWinsOverDefault() [Fact] public async Task ForwardSelectorWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -326,8 +315,7 @@ public async Task ForwardSelectorWinsOverDefault() [Fact] public async Task NullForwardSelectorUsesDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -382,8 +370,7 @@ public async Task NullForwardSelectorUsesDefault() [Fact] public async Task SpecificForwardWinsOverSelectorAndDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; diff --git a/src/Security/Authentication/test/PolicyTests.cs b/src/Security/Authentication/test/PolicyTests.cs index 2d7c22533e26..240ffa18d22b 100644 --- a/src/Security/Authentication/test/PolicyTests.cs +++ b/src/Security/Authentication/test/PolicyTests.cs @@ -54,8 +54,7 @@ public async Task CanDispatch() [Fact] public async Task DefaultTargetSelectorWinsOverDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -111,8 +110,7 @@ public async Task DefaultTargetSelectorWinsOverDefaultTarget() [Fact] public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -168,8 +166,7 @@ public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() [Fact] public async Task SpecificTargetAlwaysWinsOverDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -230,8 +227,7 @@ public async Task SpecificTargetAlwaysWinsOverDefaultTarget() [Fact] public async Task VirtualSchemeTargetsForwardWithDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -283,8 +279,7 @@ public async Task VirtualSchemeTargetsForwardWithDefaultTarget() [Fact] public async Task VirtualSchemeTargetsOverrideDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); diff --git a/src/Security/Authentication/test/SharedAuthenticationTests.cs b/src/Security/Authentication/test/SharedAuthenticationTests.cs index 395f021879d1..6a9db37426d9 100644 --- a/src/Security/Authentication/test/SharedAuthenticationTests.cs +++ b/src/Security/Authentication/test/SharedAuthenticationTests.cs @@ -26,8 +26,7 @@ public abstract class SharedAuthenticationTests where TOptions : Authe [Fact] public async Task CanForwardDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { @@ -84,8 +83,7 @@ public async Task ForwardSignInWinsOverDefault() { if (SupportsSignIn) { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { @@ -128,8 +126,7 @@ public async Task ForwardSignOutWinsOverDefault() { if (SupportsSignOut) { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -169,8 +166,7 @@ public async Task ForwardSignOutWinsOverDefault() [Fact] public async Task ForwardForbidWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -219,8 +215,7 @@ public Task TransformAsync(ClaimsPrincipal principal) [Fact] public async Task ForwardAuthenticateOnlyRunsTransformOnceByDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var transform = new RunOnce(); var builder = services.AddSingleton(transform).AddAuthentication(o => { @@ -250,8 +245,7 @@ public async Task ForwardAuthenticateOnlyRunsTransformOnceByDefault() [Fact] public async Task ForwardAuthenticateWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -290,8 +284,7 @@ public async Task ForwardAuthenticateWinsOverDefault() [Fact] public async Task ForwardChallengeWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -330,8 +323,7 @@ public async Task ForwardChallengeWinsOverDefault() [Fact] public async Task ForwardSelectorWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -400,8 +392,7 @@ public async Task ForwardSelectorWinsOverDefault() [Fact] public async Task NullForwardSelectorUsesDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -470,8 +461,7 @@ public async Task NullForwardSelectorUsesDefault() [Fact] public async Task SpecificForwardWinsOverSelectorAndDefault() { - var services = new ServiceCollection().AddLogging(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -545,8 +535,7 @@ public async Task SpecificForwardWinsOverSelectorAndDefault() [Fact] public async Task VerifySchemeDefaults() { - var services = new ServiceCollection(); - services.AddSingleton(new ConfigurationManager()); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(); RegisterAuth(builder, o => { }); var sp = services.BuildServiceProvider(); diff --git a/src/Security/Authentication/test/TestExtensions.cs b/src/Security/Authentication/test/TestExtensions.cs index a41caa407276..e7cf730e1dbc 100644 --- a/src/Security/Authentication/test/TestExtensions.cs +++ b/src/Security/Authentication/test/TestExtensions.cs @@ -7,6 +7,8 @@ using System.Xml.Linq; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Authentication; @@ -77,4 +79,12 @@ public static Task DescribeAsync(this HttpResponse res, IEnumerable(new ConfigurationManager()); + } + } From ae689f750a83e8430453d2c05858939ba34c5bc5 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 9 Jun 2022 17:48:12 -0700 Subject: [PATCH 3/5] Update API and fix functional test --- .../IAuthenticationConfigurationProvider.cs | 14 +++-------- .../src/PublicAPI.Unshipped.txt | 3 +-- .../Core/src/AuthenticationBuilder.cs | 6 +++-- ...ticationConfigurationProviderExtensions.cs | 25 +++++++++++++++++++ .../src/AuthenticationConfigureOptions.cs | 2 +- ...aultAuthenticationConfigurationProvider.cs | 11 +------- .../Core/src/PublicAPI.Unshipped.txt | 2 ++ .../src/JwtBearerConfigureOptions.cs | 2 +- 8 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs diff --git a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs index 895da5d5ad07..a9e1284be68a 100644 --- a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs +++ b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs @@ -7,21 +7,13 @@ namespace Microsoft.AspNetCore.Authentication; /// /// Provides an interface for implmenting a construct that provides -/// access to specific configuration sections. +/// access to authentication-related configuration sections. /// public interface IAuthenticationConfigurationProvider { /// - /// Returns the specified object. - /// - /// The path to the section to be returned. - /// The specified object, or null if the requested section does not exist. - IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme); - - /// - /// Returns the where authentication + /// Gets the where authentication /// options are stored. /// - /// The specified object, or null if the requested section does not exist. - IConfiguration GetAuthenticationConfiguration(); + IConfiguration AuthenticationConfiguration { get; } } diff --git a/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt index 5c70bd0c6393..e1d41588c975 100644 --- a/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,4 +1,3 @@ #nullable enable Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider -Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider.GetAuthenticationConfiguration() -> Microsoft.Extensions.Configuration.IConfiguration! -Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider.GetAuthenticationSchemeConfiguration(string! authenticationScheme) -> Microsoft.Extensions.Configuration.IConfiguration! +Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider.AuthenticationConfiguration.get -> Microsoft.Extensions.Configuration.IConfiguration! diff --git a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs index face6057be59..acbd2d491b86 100644 --- a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs +++ b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs @@ -20,8 +20,6 @@ public class AuthenticationBuilder public AuthenticationBuilder(IServiceCollection services) { Services = services; - // Always try to read global authentication options from configuration - Services.TryAddEnumerable(ServiceDescriptor.Singleton, AuthenticationConfigureOptions>()); } /// @@ -34,6 +32,10 @@ public AuthenticationBuilder(IServiceCollection services) where THandler : class, IAuthenticationHandler { var state = new AddSchemeHelperState(typeof(THandler)); + // Don't read global authentication options until an + // authentication scheme has been configured to avoid + // prematurely registering options + Services.TryAddEnumerable(ServiceDescriptor.Singleton, AuthenticationConfigureOptions>()); Services.Configure(o => { o.AddScheme(authenticationScheme, scheme => diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs new file mode 100644 index 000000000000..698bfbec75a6 --- /dev/null +++ b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.Configuration; + +namespace Microsoft.AspNetCore.Authentication; + +/// +/// Extension methods for +/// +public static class AuthenticationConfigurationProviderExtensions +{ + private const string AuthenticationSchemesKey = "Authentication:Schemes"; + + /// + /// Returns the specified object. + /// + /// An instance. + /// The path to the section to be returned. + /// The specified object, or null if the requested section does not exist. + public static IConfiguration GetSchemeConfiguration(this IAuthenticationConfigurationProvider provider, string authenticationScheme) + { + return provider.AuthenticationConfiguration.GetSection($"{AuthenticationSchemesKey}:{authenticationScheme}"); + } +} diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs index 3d34992a1a33..9cd0bebd8715 100644 --- a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs +++ b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs @@ -17,7 +17,7 @@ public AuthenticationConfigureOptions(IAuthenticationConfigurationProvider confi public void Configure(AuthenticationOptions options) { - var authenticationConfig = _authenticationConfigurationProvider.GetAuthenticationConfiguration(); + var authenticationConfig = _authenticationConfigurationProvider.AuthenticationConfiguration; var defaultScheme = authenticationConfig[DefaultSchemeKey]; if (!string.IsNullOrEmpty(defaultScheme)) { diff --git a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs index 98840286a8a1..99472b8ef149 100644 --- a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs +++ b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs @@ -9,20 +9,11 @@ internal sealed class DefaultAuthenticationConfigurationProvider : IAuthenticati { private readonly IConfiguration _configuration; private const string AuthenticationKey = "Authentication"; - private const string AuthenticationSchemesKey = "Authentication:Schemes"; public DefaultAuthenticationConfigurationProvider(IConfiguration configuration) { _configuration = configuration; } - public IConfiguration GetAuthenticationConfiguration() - { - return _configuration.GetSection(AuthenticationKey); - } - - public IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme) - { - return _configuration.GetSection($"{AuthenticationSchemesKey}:{authenticationScheme}"); - } + public IConfiguration AuthenticationConfiguration => _configuration.GetSection(AuthenticationKey); } diff --git a/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt b/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..3ea4be28d3a7 100644 --- a/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authentication/Core/src/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ #nullable enable +Microsoft.AspNetCore.Authentication.AuthenticationConfigurationProviderExtensions +static Microsoft.AspNetCore.Authentication.AuthenticationConfigurationProviderExtensions.GetSchemeConfiguration(this Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider! provider, string! authenticationScheme) -> Microsoft.Extensions.Configuration.IConfiguration! diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs index 7004f06a40be..995eed420393 100644 --- a/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs +++ b/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs @@ -35,7 +35,7 @@ public void Configure(string? name, JwtBearerOptions options) return; } - var configSection = _authenticationConfigurationProvider.GetAuthenticationSchemeConfiguration(name); + var configSection = _authenticationConfigurationProvider.GetSchemeConfiguration(name); if (configSection is null || !configSection.GetChildren().Any()) { From 81d027701421c5b41bc56cf895a91af0dde0f4a2 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 9 Jun 2022 23:26:40 -0700 Subject: [PATCH 4/5] Add null-check and regsiter ACO on AddAuth --- .../Authentication/Core/src/AuthenticationBuilder.cs | 4 ---- .../src/AuthenticationConfigurationProviderExtensions.cs | 7 +++++++ .../Core/src/AuthenticationServiceCollectionExtensions.cs | 3 +++ .../Authentication/test/RemoteAuthenticationTests.cs | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs index acbd2d491b86..91c519de823d 100644 --- a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs +++ b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs @@ -32,10 +32,6 @@ public AuthenticationBuilder(IServiceCollection services) where THandler : class, IAuthenticationHandler { var state = new AddSchemeHelperState(typeof(THandler)); - // Don't read global authentication options until an - // authentication scheme has been configured to avoid - // prematurely registering options - Services.TryAddEnumerable(ServiceDescriptor.Singleton, AuthenticationConfigureOptions>()); Services.Configure(o => { o.AddScheme(authenticationScheme, scheme => diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs index 698bfbec75a6..ccca635be830 100644 --- a/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs +++ b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs @@ -20,6 +20,13 @@ public static class AuthenticationConfigurationProviderExtensions /// The specified object, or null if the requested section does not exist. public static IConfiguration GetSchemeConfiguration(this IAuthenticationConfigurationProvider provider, string authenticationScheme) { + ArgumentNullException.ThrowIfNull(provider, nameof(provider)); + + if (provider.AuthenticationConfiguration is null) + { + throw new InvalidOperationException("There was no top-level authentication property found in configuration."); + } + return provider.AuthenticationConfiguration.GetSection($"{AuthenticationSchemesKey}:{authenticationScheme}"); } } diff --git a/src/Security/Authentication/Core/src/AuthenticationServiceCollectionExtensions.cs b/src/Security/Authentication/Core/src/AuthenticationServiceCollectionExtensions.cs index 2e34894d99b2..7a31344f6823 100644 --- a/src/Security/Authentication/Core/src/AuthenticationServiceCollectionExtensions.cs +++ b/src/Security/Authentication/Core/src/AuthenticationServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; namespace Microsoft.Extensions.DependencyInjection; @@ -28,6 +29,8 @@ public static AuthenticationBuilder AddAuthentication(this IServiceCollection se services.AddWebEncoders(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton, AuthenticationConfigureOptions>()); + return new AuthenticationBuilder(services); } diff --git a/src/Security/Authentication/test/RemoteAuthenticationTests.cs b/src/Security/Authentication/test/RemoteAuthenticationTests.cs index 51b736c9c0d7..f4577c7ff1cb 100644 --- a/src/Security/Authentication/test/RemoteAuthenticationTests.cs +++ b/src/Security/Authentication/test/RemoteAuthenticationTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -18,6 +19,7 @@ private Task CreateHost(Action configureOptions, Func CreateHostWithServices(s => { var builder = s.AddAuthentication(); + s.AddSingleton(new ConfigurationManager()); if (isDefault) { s.Configure(o => o.DefaultScheme = DefaultScheme); From be7faa8cf39b0be719f2c7cd4f929318f6eb7bda Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 13 Jun 2022 09:45:16 -0700 Subject: [PATCH 5/5] Update tests and implementation --- ...eADAuthenticationBuilderExtensionsTests.cs | 68 +++++++------------ ...B2CAuthenticationBuilderExtensionsTests.cs | 51 ++++++-------- .../src/AuthenticationConfigureOptions.cs | 4 +- .../test/SharedAuthenticationTests.cs | 33 +++++++++ 4 files changed, 83 insertions(+), 73 deletions(-) diff --git a/src/Azure/AzureAD/Authentication.AzureAD.UI/test/AzureADAuthenticationBuilderExtensionsTests.cs b/src/Azure/AzureAD/Authentication.AzureAD.UI/test/AzureADAuthenticationBuilderExtensionsTests.cs index 616e24982958..312f80d15368 100644 --- a/src/Azure/AzureAD/Authentication.AzureAD.UI/test/AzureADAuthenticationBuilderExtensionsTests.cs +++ b/src/Azure/AzureAD/Authentication.AzureAD.UI/test/AzureADAuthenticationBuilderExtensionsTests.cs @@ -20,9 +20,7 @@ public class AzureADAuthenticationBuilderExtensionsTests public void AddAzureAD_AddsAllAuthenticationHandlers() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -39,8 +37,7 @@ public void AddAzureAD_AddsAllAuthenticationHandlers() public void AddAzureAD_ConfiguresAllOptions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -89,8 +86,7 @@ public void AddAzureAD_ConfiguresAllOptions() public void AddAzureAD_AllowsOverridingCookiesAndOpenIdConnectSettings() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -134,8 +130,7 @@ public void AddAzureAD_AllowsOverridingCookiesAndOpenIdConnectSettings() public void AddAzureAD_RegisteringAddCookiesAndAddOpenIdConnectHasNoImpactOnAzureAAExtensions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -181,8 +176,7 @@ public void AddAzureAD_RegisteringAddCookiesAndAddOpenIdConnectHasNoImpactOnAzur public void AddAzureAD_ThrowsForDuplicatedSchemes() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureAD(o => { }) @@ -202,8 +196,7 @@ public void AddAzureAD_ThrowsForDuplicatedSchemes() public void AddAzureAD_ThrowsWhenOpenIdSchemeIsAlreadyInUse() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureAD(o => { }) @@ -226,8 +219,7 @@ public void AddAzureAD_ThrowsWhenOpenIdSchemeIsAlreadyInUse() public void AddAzureAD_ThrowsWhenCookieSchemeIsAlreadyInUse() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureAD(o => { }) @@ -250,8 +242,7 @@ public void AddAzureAD_ThrowsWhenCookieSchemeIsAlreadyInUse() public void AddAzureAD_ThrowsWhenInstanceIsNotSet() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureAD(o => { }); @@ -271,8 +262,7 @@ public void AddAzureAD_ThrowsWhenInstanceIsNotSet() [Fact] public void AddAzureAD_SkipsOptionsValidationForNonAzureCookies() { - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureAD(o => { }) @@ -288,9 +278,7 @@ public void AddAzureAD_SkipsOptionsValidationForNonAzureCookies() public void AddAzureADBearer_AddsAllAuthenticationHandlers() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -306,9 +294,7 @@ public void AddAzureADBearer_AddsAllAuthenticationHandlers() public void AddAzureADBearer_ConfiguresAllOptions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -342,9 +328,7 @@ public void AddAzureADBearer_ConfiguresAllOptions() public void AddAzureADBearer_CanOverrideJwtBearerOptionsConfiguration() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -376,9 +360,7 @@ public void AddAzureADBearer_CanOverrideJwtBearerOptionsConfiguration() public void AddAzureADBearer_RegisteringJwtBearerHasNoImpactOnAzureAAExtensions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -411,8 +393,7 @@ public void AddAzureADBearer_RegisteringJwtBearerHasNoImpactOnAzureAAExtensions( public void AddAzureADBearer_ThrowsForDuplicatedSchemes() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADBearer(o => { }) @@ -432,8 +413,7 @@ public void AddAzureADBearer_ThrowsForDuplicatedSchemes() public void AddAzureADBearer_ThrowsWhenBearerSchemeIsAlreadyInUse() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADBearer(o => { }) @@ -456,8 +436,7 @@ public void AddAzureADBearer_ThrowsWhenBearerSchemeIsAlreadyInUse() public void AddAzureADBearer_ThrowsWhenInstanceIsNotSet() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADBearer(o => { }); @@ -477,9 +456,7 @@ public void AddAzureADBearer_ThrowsWhenInstanceIsNotSet() [Fact] public void AddAzureADBearer_SkipsOptionsValidationForNonAzureCookies() { - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADBearer(o => { }) @@ -494,8 +471,7 @@ public void AddAzureADBearer_SkipsOptionsValidationForNonAzureCookies() [Fact] public void AddAzureAD_SkipsOptionsValidationForNonAzureOpenIdConnect() { - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureAD(o => { }) @@ -510,4 +486,12 @@ public void AddAzureAD_SkipsOptionsValidationForNonAzureOpenIdConnect() Assert.NotNull(openIdConnectOptions.Get("other")); } + + private IServiceCollection GenerateServicesForTest() + { + var services = new ServiceCollection(); + services.AddSingleton(new NullLoggerFactory()); + services.AddSingleton(new ConfigurationManager()); + return services; + } } diff --git a/src/Azure/AzureAD/Authentication.AzureADB2C.UI/test/AzureAdB2CAuthenticationBuilderExtensionsTests.cs b/src/Azure/AzureAD/Authentication.AzureADB2C.UI/test/AzureAdB2CAuthenticationBuilderExtensionsTests.cs index 36abc8d6d58d..30a13934f42d 100644 --- a/src/Azure/AzureAD/Authentication.AzureADB2C.UI/test/AzureAdB2CAuthenticationBuilderExtensionsTests.cs +++ b/src/Azure/AzureAD/Authentication.AzureADB2C.UI/test/AzureAdB2CAuthenticationBuilderExtensionsTests.cs @@ -20,8 +20,7 @@ public class AzureADB2CAuthenticationBuilderExtensionsTests public void AddAzureADB2C_AddsAllAuthenticationHandlers() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -38,8 +37,7 @@ public void AddAzureADB2C_AddsAllAuthenticationHandlers() public void AddAzureADB2C_ConfiguresAllOptions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -102,8 +100,7 @@ public void AddAzureADB2C_ConfiguresAllOptions() public void AddAzureADB2C_AllowsOverridingCookiesAndOpenIdConnectSettings() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -146,8 +143,7 @@ public void AddAzureADB2C_AllowsOverridingCookiesAndOpenIdConnectSettings() public void AddAzureADB2C_RegisteringAddCookiesAndAddOpenIdConnectHasNoImpactOnAzureAAExtensions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -192,8 +188,7 @@ public void AddAzureADB2C_RegisteringAddCookiesAndAddOpenIdConnectHasNoImpactOnA public void AddAzureADB2C_ThrowsForDuplicatedSchemes() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADB2C(o => { }) @@ -213,8 +208,7 @@ public void AddAzureADB2C_ThrowsForDuplicatedSchemes() public void AddAzureADB2C_ThrowsWhenOpenIdSchemeIsAlreadyInUse() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADB2C(o => { }) @@ -237,8 +231,7 @@ public void AddAzureADB2C_ThrowsWhenOpenIdSchemeIsAlreadyInUse() public void AddAzureADB2C_ThrowsWhenCookieSchemeIsAlreadyInUse() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADB2C(o => { }) @@ -261,9 +254,7 @@ public void AddAzureADB2C_ThrowsWhenCookieSchemeIsAlreadyInUse() public void AddAzureADB2CBearer_AddsAllAuthenticationHandlers() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -279,9 +270,7 @@ public void AddAzureADB2CBearer_AddsAllAuthenticationHandlers() public void AddAzureADB2CBearer_ConfiguresAllOptions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -316,9 +305,7 @@ public void AddAzureADB2CBearer_ConfiguresAllOptions() public void AddAzureADB2CBearer_CanOverrideJwtBearerOptionsConfiguration() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -350,9 +337,7 @@ public void AddAzureADB2CBearer_CanOverrideJwtBearerOptionsConfiguration() public void AddAzureADB2CBearer_RegisteringJwtBearerHasNoImpactOnAzureAAExtensions() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); - services.AddSingleton(new ConfigurationManager()); + var services = GenerateServicesForTest(); // Act services.AddAuthentication() @@ -385,8 +370,7 @@ public void AddAzureADB2CBearer_RegisteringJwtBearerHasNoImpactOnAzureAAExtensio public void AddAzureADB2CBearer_ThrowsForDuplicatedSchemes() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADB2CBearer(o => { }) @@ -406,8 +390,7 @@ public void AddAzureADB2CBearer_ThrowsForDuplicatedSchemes() public void AddAzureADB2CBearer_ThrowsWhenBearerSchemeIsAlreadyInUse() { // Arrange - var services = new ServiceCollection(); - services.AddSingleton(new NullLoggerFactory()); + var services = GenerateServicesForTest(); services.AddAuthentication() .AddAzureADB2CBearer(o => { }) @@ -425,4 +408,12 @@ public void AddAzureADB2CBearer_ThrowsWhenBearerSchemeIsAlreadyInUse() Assert.Equal(expectedMessage, exception.Message); } + + private IServiceCollection GenerateServicesForTest() + { + var services = new ServiceCollection(); + services.AddSingleton(new NullLoggerFactory()); + services.AddSingleton(new ConfigurationManager()); + return services; + } } diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs index 9cd0bebd8715..4224b59c6859 100644 --- a/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs +++ b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs @@ -19,7 +19,9 @@ public void Configure(AuthenticationOptions options) { var authenticationConfig = _authenticationConfigurationProvider.AuthenticationConfiguration; var defaultScheme = authenticationConfig[DefaultSchemeKey]; - if (!string.IsNullOrEmpty(defaultScheme)) + // Only set the default scheme from config if it has not + // already been set and is provided in options + if (!string.IsNullOrEmpty(defaultScheme) && string.IsNullOrEmpty(options.DefaultScheme)) { options.DefaultScheme = defaultScheme; } diff --git a/src/Security/Authentication/test/SharedAuthenticationTests.cs b/src/Security/Authentication/test/SharedAuthenticationTests.cs index 6a9db37426d9..13570baab640 100644 --- a/src/Security/Authentication/test/SharedAuthenticationTests.cs +++ b/src/Security/Authentication/test/SharedAuthenticationTests.cs @@ -600,4 +600,37 @@ public async Task CanOverrideDefaultInConfigViaAddAuthentication() var defaultSchemeFromServices = await schemeProvider.GetDefaultAuthenticateSchemeAsync(); Assert.Equal(defaultSchemeFromAddAuth, defaultSchemeFromServices.Name); } + + [Fact] + public async Task DoesNotOverrideDefaultSchemeSetViaOptions() + { + // Arrange + var defaultSchemeFromConfig = "DefaultSchemeFromConfig"; + var defaultSchemeFromOptions = "DefaultSchemeFromOptions"; + var services = new ServiceCollection(); + var config = new ConfigurationBuilder().AddInMemoryCollection(new[] + { + new KeyValuePair("Authentication:DefaultScheme", defaultSchemeFromConfig) + }).Build(); + services.AddSingleton(config); + + // Act + services.Configure(options => + { + options.DefaultScheme = defaultSchemeFromOptions; + }); + var builder = services.AddAuthentication(o => + { + o.AddScheme(defaultSchemeFromConfig, defaultSchemeFromConfig); + o.AddScheme(defaultSchemeFromOptions, defaultSchemeFromOptions); + }); + RegisterAuth(builder, _ => { }); + var sp = services.BuildServiceProvider(); + + // Assert + var schemeProvider = sp.GetRequiredService(); + var defaultSchemeFromServices = await schemeProvider.GetDefaultAuthenticateSchemeAsync(); + Assert.Equal(defaultSchemeFromOptions, defaultSchemeFromServices.Name); + + } }