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/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..a9e1284be68a 100644 --- a/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs +++ b/src/Http/Authentication.Abstractions/src/IAuthenticationConfigurationProvider.cs @@ -7,14 +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. + /// Gets the where authentication + /// options are stored. /// - /// The path to the section to be returned. - /// The specified object, or null if the requested section does not exist. - IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme); + IConfiguration AuthenticationConfiguration { get; } } diff --git a/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt index efe32d90a931..e1d41588c975 100644 --- a/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Authentication.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,3 +1,3 @@ #nullable enable Microsoft.AspNetCore.Authentication.IAuthenticationConfigurationProvider -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 9a2c7deea1fe..91c519de823d 100644 --- a/src/Security/Authentication/Core/src/AuthenticationBuilder.cs +++ b/src/Security/Authentication/Core/src/AuthenticationBuilder.cs @@ -18,7 +18,9 @@ public class AuthenticationBuilder /// /// The services being configured. public AuthenticationBuilder(IServiceCollection services) - => Services = services; + { + Services = services; + } /// /// The services being configured. diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs new file mode 100644 index 000000000000..ccca635be830 --- /dev/null +++ b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs @@ -0,0 +1,32 @@ +// 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) + { + 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/AuthenticationConfigureOptions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs new file mode 100644 index 000000000000..4224b59c6859 --- /dev/null +++ b/src/Security/Authentication/Core/src/AuthenticationConfigureOptions.cs @@ -0,0 +1,29 @@ +// 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; + private const string DefaultSchemeKey = "DefaultScheme"; + + public AuthenticationConfigureOptions(IAuthenticationConfigurationProvider configurationProvider) + { + _authenticationConfigurationProvider = configurationProvider; + } + + public void Configure(AuthenticationOptions options) + { + var authenticationConfig = _authenticationConfigurationProvider.AuthenticationConfiguration; + var defaultScheme = authenticationConfig[DefaultSchemeKey]; + // 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/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/Core/src/DefaultAuthenticationConfigurationProvider.cs b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs index 057e1a5ad1d4..99472b8ef149 100644 --- a/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs +++ b/src/Security/Authentication/Core/src/DefaultAuthenticationConfigurationProvider.cs @@ -8,14 +8,12 @@ namespace Microsoft.AspNetCore.Authentication; internal sealed class DefaultAuthenticationConfigurationProvider : IAuthenticationConfigurationProvider { private readonly IConfiguration _configuration; + private const string AuthenticationKey = "Authentication"; public DefaultAuthenticationConfigurationProvider(IConfiguration configuration) { _configuration = configuration; } - public IConfiguration GetAuthenticationSchemeConfiguration(string authenticationScheme) - { - return _configuration.GetSection($"Authentication:Schemes:{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/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/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()) { 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..823f5c50e8ae 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; @@ -24,7 +25,7 @@ public class ClientCertificateAuthenticationTests [Fact] public async Task VerifySchemeDefaults() { - var services = new ServiceCollection(); + var services = new ServiceCollection().ConfigureAuthTestServices(); 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..95be59c52e2f 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; @@ -27,8 +28,7 @@ private void ConfigureDefaults(OpenIdConnectOptions o) [Fact] public async Task CanForwardDefault() { - var services = new ServiceCollection().AddLogging(); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -71,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; @@ -101,8 +100,7 @@ public async Task ForwardSignInThrows() [Fact] public async Task ForwardSignOutWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -142,8 +140,7 @@ public async Task ForwardSignOutWinsOverDefault() [Fact] public async Task ForwardForbidWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -183,8 +180,7 @@ public async Task ForwardForbidWinsOverDefault() [Fact] public async Task ForwardAuthenticateWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); - + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -224,7 +220,7 @@ public async Task ForwardAuthenticateWinsOverDefault() [Fact] public async Task ForwardChallengeWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -264,7 +260,7 @@ public async Task ForwardChallengeWinsOverDefault() [Fact] public async Task ForwardSelectorWinsOverDefault() { - var services = new ServiceCollection().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -319,7 +315,7 @@ public async Task ForwardSelectorWinsOverDefault() [Fact] public async Task NullForwardSelectorUsesDefault() { - var services = new ServiceCollection().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; @@ -374,7 +370,7 @@ public async Task NullForwardSelectorUsesDefault() [Fact] public async Task SpecificForwardWinsOverSelectorAndDefault() { - var services = new ServiceCollection().AddLogging(); + 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 79030893499f..240ffa18d22b 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; @@ -53,7 +54,7 @@ public async Task CanDispatch() [Fact] public async Task DefaultTargetSelectorWinsOverDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -109,7 +110,7 @@ public async Task DefaultTargetSelectorWinsOverDefaultTarget() [Fact] public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -165,7 +166,7 @@ public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() [Fact] public async Task SpecificTargetAlwaysWinsOverDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -226,7 +227,7 @@ public async Task SpecificTargetAlwaysWinsOverDefaultTarget() [Fact] public async Task VirtualSchemeTargetsForwardWithDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); @@ -278,7 +279,7 @@ public async Task VirtualSchemeTargetsForwardWithDefaultTarget() [Fact] public async Task VirtualSchemeTargetsOverrideDefaultTarget() { - var services = new ServiceCollection().AddOptions().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); services.AddAuthentication(o => { o.AddScheme("auth1", "auth1"); 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); diff --git a/src/Security/Authentication/test/SharedAuthenticationTests.cs b/src/Security/Authentication/test/SharedAuthenticationTests.cs index f1085b604169..13570baab640 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; @@ -25,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 => { @@ -83,7 +83,7 @@ public async Task ForwardSignInWinsOverDefault() { if (SupportsSignIn) { - var services = new ServiceCollection().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { @@ -126,7 +126,7 @@ public async Task ForwardSignOutWinsOverDefault() { if (SupportsSignOut) { - var services = new ServiceCollection().AddLogging(); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(o => { o.DefaultScheme = DefaultScheme; @@ -166,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; @@ -216,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 => { @@ -247,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; @@ -287,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; @@ -327,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; @@ -397,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; @@ -467,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; @@ -542,7 +535,7 @@ public async Task SpecificForwardWinsOverSelectorAndDefault() [Fact] public async Task VerifySchemeDefaults() { - var services = new ServiceCollection(); + var services = new ServiceCollection().ConfigureAuthTestServices(); var builder = services.AddAuthentication(); RegisterAuth(builder, o => { }); var sp = services.BuildServiceProvider(); @@ -552,4 +545,92 @@ 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); + } + + [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); + + } } 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()); + } + } 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();