Describe the bug I am trying to build an application which performs a check against an API Key for a subset of endpoints. If the API Key does not match, return a 401.
However, every request is returning a 401. It seems from debugging that MyCustomReactiveAuthManager#authenticate is never being triggered.
Versions:
- Spring Boot -
3.2.4 - Spring Security -
6.2.3
To Reproduce
Here is my simplified SecurityConfig.java. I call any endpoint on my API and it returns a 401. The log is never hit.
Note that when I replace .authenticated() with .permitAll() that Auth is skipped, meaning that this chain is definitely being executed.
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain webFilterChain(
ServerHttpSecurity http,
AuthenticationWebFilter myCustomAuthWebFilter
) {
return http
.authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.addFilterAt(myCustomAuthWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
.build();
}
@Bean
public AuthenticationWebFilter myCustomAuthWebFilter(ReactiveAuthenticationManager myCustomReactiveAuthManager) {
return new AuthenticationWebFilter(myCustomReactiveAuthManager);
}
@Bean
public ReactiveAuthenticationManager myCustomReactiveAuthManager() {
return new MyCustomReactiveAuthManager();
}
public static class MyCustomReactiveAuthManager implements ReactiveAuthenticationManager {
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
System.out.println("This is never hit");
return Mono.empty();
}
}
}
Expected behavior
The line System.out.println("This is never hit"); is executed. In reality, this will be swapped out for the API Key check.
Comment From: pkelly504
Solved ✅ - I had to add the following to my Custom Web Filter Bean:
@Bean
public AuthenticationWebFilter myCustomAuthWebFilter(ReactiveAuthenticationManager myCustomReactiveAuthManager) {
AuthenticationWebFilter filter = new AuthenticationWebFilter(myCustomReactiveAuthManager);
filter.setServerAuthenticationConverter(exchange -> {
// Extract API Key and convert to Authentication Object
});
return filter;
}
By default, it appears to use the ServerHttpBasicAuthenticationConverter which looks for Basic Auth credentials.