diff --git a/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs b/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs index 8d5281847077..0ffeeb74ecc6 100644 --- a/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs +++ b/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs @@ -79,6 +79,17 @@ protected override async Task HandleAuthenticateAsync() } var result = await ValidateCertificateAsync(clientCertificate); + + // Invoke the failed handler if validation failed, before updating the cache + if (result.Failure != null) + { + var authenticationFailedContext = await HandleFailureAsync(result.Failure); + if (authenticationFailedContext.Result != null) + { + result = authenticationFailedContext.Result; + } + } + if (_cache != null) { _cache.Put(Context, clientCertificate, result); @@ -87,12 +98,7 @@ protected override async Task HandleAuthenticateAsync() } catch (Exception ex) { - var authenticationFailedContext = new CertificateAuthenticationFailedContext(Context, Scheme, Options) - { - Exception = ex - }; - - await Events.AuthenticationFailed(authenticationFailedContext); + var authenticationFailedContext = await HandleFailureAsync(ex); if (authenticationFailedContext.Result != null) { return authenticationFailedContext.Result; @@ -102,6 +108,17 @@ protected override async Task HandleAuthenticateAsync() } } + private async Task HandleFailureAsync(Exception error) + { + var authenticationFailedContext = new CertificateAuthenticationFailedContext(Context, Scheme, Options) + { + Exception = error + }; + + await Events.AuthenticationFailed(authenticationFailedContext); + return authenticationFailedContext; + } + private async Task ValidateCertificateAsync(X509Certificate2 clientCertificate) { // If we have a self signed cert, and they're not allowed, exit early and not bother with diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerEvents.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerEvents.cs index be8700528c55..6fc402ad1f60 100755 --- a/src/Security/Authentication/JwtBearer/src/JwtBearerEvents.cs +++ b/src/Security/Authentication/JwtBearer/src/JwtBearerEvents.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer public class JwtBearerEvents { /// - /// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. + /// Invoked if authentication fails during request processing. The exceptions will be re-thrown after this event unless suppressed. /// public Func OnAuthenticationFailed { get; set; } = context => Task.CompletedTask; diff --git a/src/Security/Authentication/test/CertificateTests.cs b/src/Security/Authentication/test/CertificateTests.cs index fc1dd46e0b1c..6b805aaa623d 100644 --- a/src/Security/Authentication/test/CertificateTests.cs +++ b/src/Security/Authentication/test/CertificateTests.cs @@ -284,6 +284,30 @@ public async Task VerifyUntrustedClientCertEndsUpInForbidden() var response = await server.CreateClient().GetAsync("https://example.com/"); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } + + [Fact] + public async Task VerifyValidationFailureCanBeHandled() + { + var failCalled = false; + using var host = await CreateHost( + new CertificateAuthenticationOptions + { + Events = new CertificateAuthenticationEvents() + { + OnAuthenticationFailed = context => + { + context.Fail("Validation failed: " + context.Exception); + failCalled = true; + return Task.CompletedTask; + } + } + }, Certificates.SignedClient); + + using var server = host.GetTestServer(); + var response = await server.CreateClient().GetAsync("https://example.com/"); + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + Assert.True(failCalled); + } [Fact] public async Task VerifyClientCertWithUntrustedRootAndTrustedChainEndsUpInForbidden() @@ -752,7 +776,6 @@ private static async Task CreateHost( await host.StartAsync(); - var server = host.GetTestServer(); server.BaseAddress = baseAddress; return host;