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.