Describe the bug
Authenticate an unauthorized Authentication using ServerSecurityContextRepository cause the Mono<SecurityContext> produced by its load method subscribed twice.
To Reproduce
See the following config snippet:
@EnableWebFluxSecurity
public class TestSecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,
ServerSecurityContextRepository serverSecurityContextRepository) {
return http.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.securityContextRepository(serverSecurityContextRepository)
.authorizeExchange()
.anyExchange().authenticated()
.and()
.build();
}
@Bean
public ServerSecurityContextRepository serverSecurityContextRepository() {
return new ServerSecurityContextRepository() {
@Override
public Mono<Void> save(ServerWebExchange exchange,
SecurityContext context) {
return Mono.empty();
}
@Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
// no authorities -> not authenticated
final Authentication authentication = new UsernamePasswordAuthenticationToken("test", "test");
return Mono.defer(() -> {
System.out.println("TestSecurityConfig: load"); // invoked twice
return Mono.just(new SecurityContextImpl(authentication));
});
}
};
}
}
The root cause is:
ReactorContextWebFilter save the Mono of SecurityContext into the reactor context, rather than the actual value. The Mono is subscribed the first time in regular authentication process.
- The Mono in
ExceptionTranslationWebFilter on AccessDeniedException resumes to retrieve the Principal, which leads to getting the Mono of SecurityContext (and subscribe it again) in SecurityContextServerWebExchange.
Expected behavior
I would expect the load method is only called once in this scenario. Not sure if the current behavior is intended, but it's quite confusing to me.
Perhaps saving Mono<SecurityContext> (opposed to saving the SecurityContext itself) is the root of the pain. I think keeping the Mono could lead to a more ambiguous lifecycle and might eventually harm the way to make Authentication immutable as well.
Sample
See the snippet above. An empty project with such a security config and any request could trigger the issue.
Describe the bug
Authenticate an unauthorized Authentication using
ServerSecurityContextRepositorycause theMono<SecurityContext>produced by itsloadmethod subscribed twice.To Reproduce
See the following config snippet:
The root cause is:
ReactorContextWebFiltersave the Mono ofSecurityContextinto the reactor context, rather than the actual value. The Mono is subscribed the first time in regular authentication process.ExceptionTranslationWebFilteronAccessDeniedExceptionresumes to retrieve thePrincipal, which leads to getting the Mono ofSecurityContext(and subscribe it again) inSecurityContextServerWebExchange.Expected behavior
I would expect the load method is only called once in this scenario. Not sure if the current behavior is intended, but it's quite confusing to me.
Perhaps saving
Mono<SecurityContext>(opposed to saving theSecurityContextitself) is the root of the pain. I think keeping the Mono could lead to a more ambiguous lifecycle and might eventually harm the way to make Authentication immutable as well.Sample
See the snippet above. An empty project with such a security config and any request could trigger the issue.