diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 90395ee338d4..5a3c17a4d52c 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Authorization; @@ -25,13 +26,15 @@ public class AuthorizationMiddleware private readonly IAuthorizationPolicyProvider _policyProvider; private readonly bool _canCache; private readonly AuthorizationPolicyCache? _policyCache; + private readonly ILogger? _logger; /// /// Initializes a new instance of . /// /// The next middleware in the application middleware pipeline. /// The . - public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider) + public AuthorizationMiddleware(RequestDelegate next, + IAuthorizationPolicyProvider policyProvider) { _next = next ?? throw new ArgumentNullException(nameof(next)); _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); @@ -44,7 +47,24 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide /// The next middleware in the application middleware pipeline. /// The . /// The . - public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider, IServiceProvider services) : this(next, policyProvider) + /// The . + public AuthorizationMiddleware(RequestDelegate next, + IAuthorizationPolicyProvider policyProvider, + IServiceProvider services, + ILogger logger) : this(next, policyProvider, services) + { + _logger = logger; + } + + /// + /// Initializes a new instance of . + /// + /// The next middleware in the application middleware pipeline. + /// The . + /// The . + public AuthorizationMiddleware(RequestDelegate next, + IAuthorizationPolicyProvider policyProvider, + IServiceProvider services) : this(next, policyProvider) { ArgumentNullException.ThrowIfNull(services); @@ -108,7 +128,6 @@ public async Task Invoke(HttpContext context) var policyEvaluator = context.RequestServices.GetRequiredService(); var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context); - if (authenticateResult?.Succeeded ?? false) { if (context.Features.Get() is IAuthenticateResultFeature authenticateResultFeature) @@ -130,6 +149,11 @@ public async Task Invoke(HttpContext context) return; } + if (authenticateResult != null && !authenticateResult.Succeeded) + { + _logger?.LogDebug("Policy authentication schemes {policyName} did not succeed", String.Join(", ", policy.AuthenticationSchemes)); + } + object? resource; if (AppContext.TryGetSwitch(SuppressUseHttpContextAsAuthorizationResource, out var useEndpointAsResource) && useEndpointAsResource) { diff --git a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt index 302dcdf9f9ae..4388b0c584c1 100644 --- a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ #nullable enable Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.AuthorizationMiddleware(Microsoft.AspNetCore.Http.RequestDelegate! next, Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.IServiceProvider! services) -> void +Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.AuthorizationMiddleware(Microsoft.AspNetCore.Http.RequestDelegate! next, Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.IServiceProvider! services, Microsoft.Extensions.Logging.ILogger! logger) -> void static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> TBuilder static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, System.Action! configurePolicy) -> TBuilder static Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions.AddAuthorizationBuilder(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index 4fca44704768..2d8fb8a5700d 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -6,11 +6,11 @@ using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Authorization.Test.TestObjects; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Moq; @@ -46,8 +46,9 @@ public async Task NoEndpointWithFallback_AnonymousUser_Challenges() var policyProvider = new Mock(); policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true); // Act @@ -85,8 +86,9 @@ public async Task HasEndpointWithFallbackWithoutAuth_AnonymousUser_Challenges() policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint()); // Act @@ -106,8 +108,9 @@ public async Task HasEndpointWithOnlyFallbackAuth_AnonymousUser_Allows() policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); var authenticationService = new TestAuthenticationService(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); // Act @@ -126,8 +129,9 @@ public async Task HasEndpointWithAuth_AnonymousUser_Challenges() policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); var authenticationService = new TestAuthenticationService(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); // Act @@ -147,8 +151,9 @@ public async Task HasEndpointWithAuth_ChallengesAuthenticationSchemes() policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); var authenticationService = new TestAuthenticationService(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute() { AuthenticationSchemes = "whatever" }), authenticationService: authenticationService); // Act @@ -168,8 +173,9 @@ public async Task HasEndpointWithAuth_AnonymousUser_ChallengePerScheme() policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); var authenticationService = new TestAuthenticationService(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); // Act @@ -193,7 +199,8 @@ public async Task OnAuthorizationAsync_WillCallPolicyProvider() policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy) .Callback(() => getFallbackPolicyCount++); var next = new TestRequestDelegate(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var logger = new Mock>(); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever"))); // Act & Assert @@ -235,12 +242,13 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() .Callback(() => getFallbackPolicyCount++); policyProvider.Setup(p => p.AllowsCachingPolicies).Returns(true); var next = new TestRequestDelegate(); + var logger = new Mock>(); var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever")); var services = new ServiceCollection() .AddAuthorization() .AddSingleton(CreateDataSource(endpoint)).BuildServiceProvider(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, services); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, services, logger.Object); var context = GetHttpContext(anonymous: true, endpoint: endpoint); // Act & Assert @@ -299,7 +307,8 @@ public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderCanCa var services = new ServiceCollection() .AddAuthorization() .AddSingleton(CreateDataSource(endpoint)).BuildServiceProvider(); - var middleware = CreateMiddleware(next.Invoke, policyProvider, services); + var logger = new Mock>(); + var middleware = CreateMiddleware(next.Invoke, policyProvider, services, logger.Object); var context = GetHttpContext(anonymous: true, endpoint: endpoint); // Act & Assert @@ -332,7 +341,8 @@ public async Task OnAuthorizationAsync_WillCallCustomPolicyProviderWithCache() policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy) .Callback(() => getFallbackPolicyCount++); var next = new TestRequestDelegate(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var logger = new Mock>(); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever"))); // Act & Assert @@ -444,8 +454,9 @@ public async Task Invoke_AuthSchemesFailShouldSetEmptyPrincipalOnContext() policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); var next = new TestRequestDelegate(); var authenticationService = new TestAuthenticationService(); + var logger = new Mock>(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, logger: logger.Object); var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); // Act @@ -824,11 +835,11 @@ public async Task WebApplicationBuilder_CanRegisterAuthzMiddlewareWithScopedServ Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); } - private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, IServiceProvider services = null) + private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, IServiceProvider services = null, ILogger logger = null) { requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask); services ??= new ServiceCollection().BuildServiceProvider(); - return new AuthorizationMiddleware(requestDelegate, policyProvider, services); + return new AuthorizationMiddleware(requestDelegate, policyProvider, services, logger); } private Endpoint CreateEndpoint(params object[] metadata) diff --git a/src/Security/perf/Microbenchmarks/AuthorizationMiddlewareBenchmark.cs b/src/Security/perf/Microbenchmarks/AuthorizationMiddlewareBenchmark.cs index 6cc5177e0468..7c8d832cf3dd 100644 --- a/src/Security/perf/Microbenchmarks/AuthorizationMiddlewareBenchmark.cs +++ b/src/Security/perf/Microbenchmarks/AuthorizationMiddlewareBenchmark.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using BenchmarkDotNet.Attributes;