diff --git a/src/DefaultBuilder/src/PublicAPI.Unshipped.txt b/src/DefaultBuilder/src/PublicAPI.Unshipped.txt index 6eba7bee0ff8..88ff0b5ecc80 100644 --- a/src/DefaultBuilder/src/PublicAPI.Unshipped.txt +++ b/src/DefaultBuilder/src/PublicAPI.Unshipped.txt @@ -1,4 +1,3 @@ #nullable enable Microsoft.AspNetCore.Builder.WebApplication.Use(System.Func! middleware) -> Microsoft.AspNetCore.Builder.IApplicationBuilder! -Microsoft.AspNetCore.Builder.WebApplicationBuilder.Authentication.get -> Microsoft.AspNetCore.Authentication.AuthenticationBuilder! static Microsoft.Extensions.Hosting.GenericHostBuilderExtensions.ConfigureWebHostDefaults(this Microsoft.Extensions.Hosting.IHostBuilder! builder, System.Action! configure, System.Action! configureOptions) -> Microsoft.Extensions.Hosting.IHostBuilder! diff --git a/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs b/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs deleted file mode 100644 index f53de4ea8f25..000000000000 --- a/src/DefaultBuilder/src/WebApplicationAuthenticationBuilder.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Authentication; - -internal class WebApplicationAuthenticationBuilder : AuthenticationBuilder -{ - public bool IsAuthenticationConfigured { get; private set; } - - public WebApplicationAuthenticationBuilder(IServiceCollection services) : base(services) { } - - public override AuthenticationBuilder AddPolicyScheme(string authenticationScheme, string? displayName, Action configureOptions) - { - RegisterServices(); - return base.AddPolicyScheme(authenticationScheme, displayName, configureOptions); - } - - public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string? displayName, Action? configureOptions) - { - RegisterServices(); - return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions); - } - - public override AuthenticationBuilder AddScheme(string authenticationScheme, string? displayName, Action? configureOptions) - { - RegisterServices(); - return base.AddScheme(authenticationScheme, displayName, configureOptions); - } - - public override AuthenticationBuilder AddScheme(string authenticationScheme, Action? configureOptions) - { - RegisterServices(); - return base.AddScheme(authenticationScheme, configureOptions); - } - - private void RegisterServices() - { - if (!IsAuthenticationConfigured) - { - IsAuthenticationConfigured = true; - Services.AddAuthentication(); - Services.AddAuthorization(); - } - } -} diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index d081da2532a6..d002acd3a5e8 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -18,10 +19,10 @@ public sealed class WebApplicationBuilder { private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder"; private const string AuthenticationMiddlewareSetKey = "__AuthenticationMiddlewareSet"; + private const string AuthorizationMiddlewareSetKey = "__AuthorizationMiddlewareSet"; private readonly HostApplicationBuilder _hostApplicationBuilder; private readonly ServiceDescriptor _genericWebHostServiceDescriptor; - private readonly WebApplicationAuthenticationBuilder _webAuthBuilder; private WebApplication? _builtApplication; @@ -82,7 +83,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action @@ -117,11 +117,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action public ConfigureHostBuilder Host { get; } - /// - /// An for configuring authentication-related properties. - /// - public AuthenticationBuilder Authentication => _webAuthBuilder; - /// /// Builds the . /// @@ -175,15 +170,25 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui } } - if (_webAuthBuilder.IsAuthenticationConfigured) + // Process authorization and authentication middlewares independently to avoid + // registering middlewares for services that do not exist + if (_builtApplication.Services.GetService() is not null) { // Don't add more than one instance of the middleware if (!_builtApplication.Properties.ContainsKey(AuthenticationMiddlewareSetKey)) { // The Use invocations will set the property on the outer pipeline, - // but we want to set it on the inner pipeline as well + // but we want to set it on the inner pipeline as well. _builtApplication.Properties[AuthenticationMiddlewareSetKey] = true; app.UseAuthentication(); + } + } + + if (_builtApplication.Services.GetService() is not null) + { + if (!_builtApplication.Properties.ContainsKey(AuthorizationMiddlewareSetKey)) + { + _builtApplication.Properties[AuthorizationMiddlewareSetKey] = true; app.UseAuthorization(); } } diff --git a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs index 5f74a0954d0f..fd4bda6dcaa2 100644 --- a/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs +++ b/src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs @@ -1990,11 +1990,9 @@ public async Task RegisterAuthMiddlewaresCorrectly() var username = "foobar"; var builder = WebApplication.CreateBuilder(); - builder.Services.AddAuthenticationCore(o => - { - o.DefaultScheme = "testSchemeName"; - }); - builder.Authentication.AddScheme("testSchemeName", "testDisplayName", _ => { }); + builder.Services.AddAuthorization(); + builder.Services.AddAuthentication("testSchemeName") + .AddScheme("testSchemeName", "testDisplayName", _ => { }); builder.WebHost.UseTestServer(); await using var app = builder.Build(); @@ -2027,6 +2025,43 @@ public async Task RegisterAuthMiddlewaresCorrectly() Assert.True(customMiddlewareExecuted); } + [Fact] + public async Task SupportsDisablingMiddlewareAutoRegistration() + { + var builder = WebApplication.CreateBuilder(); + builder.Services.AddAuthorization(); + builder.Services.AddAuthentication("testSchemeName") + .AddScheme("testSchemeName", "testDisplayName", _ => { }); + builder.WebHost.UseTestServer(); + await using var app = builder.Build(); + + app.Use(next => + { + return async context => + { + // IAuthenticationFeature is added by the authentication middleware + // during invocation. This middleware should run after authentication + // and be able to access the feature. + var authFeature = context.Features.Get(); + Assert.Null(authFeature); + Assert.Null(context.User.Identity.Name); + await next(context); + }; + }); + + app.Properties["__AuthenticationMiddlewareSet"] = true; + + app.MapGet("/hello", (ClaimsPrincipal user) => {}).AllowAnonymous(); + + Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet")); + Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); + + await app.StartAsync(); + + Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet")); + Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); + } + private class UberHandler : AuthenticationHandler { public UberHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } diff --git a/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs b/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs index 470b722d6ef6..734c8e413d05 100644 --- a/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs +++ b/src/Security/Authentication/JwtBearer/samples/MinimalJwtBearerSample/Program.cs @@ -2,15 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Security.Claims; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Builder; var builder = WebApplication.CreateBuilder(args); -builder.Authentication.AddJwtBearer(); -builder.Authentication.AddJwtBearer("ClaimedDetails"); -builder.Authentication.AddJwtBearer("InvalidScheme"); +builder.Services.AddAuthentication() + .AddJwtBearer() + .AddJwtBearer("ClaimedDetails") + .AddJwtBearer("InvalidScheme"); builder.Services.AddAuthorization(options => options.AddPolicy("is_admin", policy => diff --git a/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs b/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs index 9c11dc1e35c1..d83f2e4a23f9 100644 --- a/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs +++ b/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs @@ -154,7 +154,7 @@ public async Task IAuthenticateResultFeature_SettingResultSetsUser() } [Fact] - public async Task WebApplicationBuilder_RegistersAuthenticationMiddlewares() + public async Task WebApplicationBuilder_RegistersAuthenticationAndAuthorizationMiddlewares() { var builder = WebApplication.CreateBuilder(); builder.Configuration.AddInMemoryCollection(new[] @@ -162,25 +162,41 @@ public async Task WebApplicationBuilder_RegistersAuthenticationMiddlewares() new KeyValuePair("Authentication:Schemes:Bearer:ClaimsIssuer", "SomeIssuer"), new KeyValuePair("Authentication:Schemes:Bearer:Audiences:0", "https://localhost:5001") }); - builder.Authentication.AddJwtBearer(); + builder.Services.AddAuthorization(); + builder.Services.AddAuthentication().AddJwtBearer(); await using var app = builder.Build(); - var webAppAuthBuilder = Assert.IsType(builder.Authentication); - Assert.True(webAppAuthBuilder.IsAuthenticationConfigured); - // Authentication middleware isn't registered until application // is built on startup Assert.False(app.Properties.ContainsKey("__AuthenticationMiddlewareSet")); + Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); await app.StartAsync(); Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet")); + Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); var options = app.Services.GetService>().Get(JwtBearerDefaults.AuthenticationScheme); Assert.Equal(new[] { "SomeIssuer" }, options.TokenValidationParameters.ValidIssuers); Assert.Equal(new[] { "https://localhost:5001" }, options.TokenValidationParameters.ValidAudiences); } + [Fact] + public async Task WebApplicationBuilder_OnlyRegistersMiddlewareWithSupportedServices() + { + var builder = WebApplication.CreateBuilder(); + builder.Services.AddAuthentication().AddJwtBearer(); + await using var app = builder.Build(); + + Assert.False(app.Properties.ContainsKey("__AuthenticationMiddlewareSet")); + Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); + + await app.StartAsync(); + + Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet")); + Assert.False(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); + } + private HttpContext GetHttpContext( Action registerServices = null, IAuthenticationService authenticationService = null) diff --git a/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs index 1e6cedfc5b18..74b23c01f871 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs @@ -12,6 +12,8 @@ namespace Microsoft.AspNetCore.Builder; /// public static class AuthorizationAppBuilderExtensions { + internal const string AuthorizationMiddlewareSetKey = "__AuthorizationMiddlewareSet"; + /// /// Adds the to the specified , which enables authorization capabilities. /// @@ -30,6 +32,7 @@ public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app) VerifyServicesRegistered(app); + app.Properties[AuthorizationMiddlewareSetKey] = true; return app.UseMiddleware(); }