1
0
mirror of synced 2026-05-22 21:33:16 +00:00

Provide Authentication to AuthenticationExceptions

Issue gh-16444
This commit is contained in:
Josh Cummings
2025-03-21 17:45:10 -06:00
parent 464e506429
commit 56e757a2a1
14 changed files with 172 additions and 28 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.Assert;
/**
@@ -58,6 +59,7 @@ public class DelegatingReactiveAuthenticationManager implements ReactiveAuthenti
public Mono<Authentication> authenticate(Authentication authentication) {
Flux<ReactiveAuthenticationManager> result = Flux.fromIterable(this.delegates);
Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))
.doOnError(this.logger::debug);
return ((this.continueOnError) ? result.concatMapDelayError(logging) : result.concatMap(logging)).next();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -202,6 +202,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
throw ex;
}
catch (AuthenticationException ex) {
ex.setAuthenticationRequest(authentication);
logger.debug(LogMessage.format("Authentication failed with provider %s since %s",
provider.getClass().getSimpleName(), ex.getMessage()));
lastException = ex;
@@ -265,6 +266,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
@SuppressWarnings("deprecation")
private void prepareException(AuthenticationException ex, Authentication auth) {
ex.setAuthenticationRequest(auth);
this.eventPublisher.publishAuthenticationFailure(ex, auth);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -108,6 +109,15 @@ public class DelegatingReactiveAuthenticationManagerTests {
assertThat(manager.authenticate(this.authentication).block()).isEqualTo(this.authentication);
}
@Test
void whenAccountStatusExceptionThenAuthenticationRequestIsIncluded() {
AuthenticationException expected = new LockedException("");
given(this.delegate1.authenticate(any())).willReturn(Mono.error(expected));
ReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1);
StepVerifier.create(manager.authenticate(this.authentication)).expectError(LockedException.class).verify();
assertThat(expected.getAuthenticationRequest()).isEqualTo(this.authentication);
}
private DelegatingReactiveAuthenticationManager managerWithContinueOnError() {
DelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,
this.delegate2);
@@ -253,6 +253,34 @@ public class ProviderManagerTests {
verify(publisher).publishAuthenticationFailure(expected, authReq);
}
@Test
void whenAccountStatusExceptionThenAuthenticationRequestIsIncluded() {
AuthenticationException expected = new LockedException("");
ProviderManager mgr = new ProviderManager(createProviderWhichThrows(expected));
Authentication authReq = mock(Authentication.class);
assertThatExceptionOfType(LockedException.class).isThrownBy(() -> mgr.authenticate(authReq));
assertThat(expected.getAuthenticationRequest()).isEqualTo(authReq);
}
@Test
void whenInternalServiceAuthenticationExceptionThenAuthenticationRequestIsIncluded() {
AuthenticationException expected = new InternalAuthenticationServiceException("");
ProviderManager mgr = new ProviderManager(createProviderWhichThrows(expected));
Authentication authReq = mock(Authentication.class);
assertThatExceptionOfType(InternalAuthenticationServiceException.class)
.isThrownBy(() -> mgr.authenticate(authReq));
assertThat(expected.getAuthenticationRequest()).isEqualTo(authReq);
}
@Test
void whenAuthenticationExceptionThenAuthenticationRequestIsIncluded() {
AuthenticationException expected = new BadCredentialsException("");
ProviderManager mgr = new ProviderManager(createProviderWhichThrows(expected));
Authentication authReq = mock(Authentication.class);
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> mgr.authenticate(authReq));
assertThat(expected.getAuthenticationRequest()).isEqualTo(authReq);
}
// SEC-2367
@Test
void providerThrowsInternalAuthenticationServiceException() {