Starting with the current Spring Boot 3.2.1-SNAPSHOT there is big difference in the security defaults between webmvc and webflux.

From the 3.2 release notes:

... spring-security-oauth2-client, spring-security-oauth2-resource-server, and spring-security-saml2-service-provider If you are using one of the above dependencies yet still require an InMemoryUserDetailsManager or MapReactiveUserDetailsService in your application, define the required bean in your application

As soon as you put spring-security-oauth2-resource-server on the classpath: - a reactive application completely backs out of applying any security - a servlet application still fails on the secure side.

Note: there was an auto-configuration problem in 3.2.0 for webflux (see https://github.com/spring-projects/spring-boot/issues/38713) - we're now validating the behaviour on 3.2.1-SNAPSHOT:

My test setup:

@SpringBootTest(webEnvironment = RANDOM_PORT)
class SpringBootApplicationTests {

        @SpringBootApplication
    static class Application {

    }

    @LocalServerPort
    int port;

    @Test
    void testUnauthorized() {
        var client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port).build();
        client.get().uri("/").exchange().expectStatus().isUnauthorized();
    }

}

With spring-boot-starter-web + spring-boot-starter-security: * Spring Boot 3.1.6 - :heavy_check_mark: HTTP 401 * Spring Boot 3.2.1-SNAPSHOT - :heavy_check_mark: HTTP 401

With spring-boot-starter-webflux + spring-boot-starter-security: * Spring Boot 3.1.6 - :heavy_check_mark: HTTP 401 * Spring Boot 3.2.1-SNAPSHOT - :heavy_check_mark: HTTP 401

With spring-boot-starter-web + spring-boot-starter-oauth2-resource-server: * Spring Boot 3.1.6 - :heavy_check_mark: HTTP 401 * Spring Boot 3.2.1-SNAPSHOT - :heavy_check_mark: HTTP 401

With spring-boot-starter-webflux + spring-boot-starter-oauth2-resource-server: * Spring Boot 3.1.6 - :heavy_check_mark: HTTP 401 * Spring Boot 3.2.1-SNAPSHOT - :question: HTTP 404

Discussion in https://github.com/spring-projects/spring-boot/issues/38713 implies that all of this is expected behaviour ? * I don't think it is desired that webmvc behaves differently from webflux ? * Can I please stress that adding a starter suddenly stripping security is VERY unexpected ? * This also means that upgrading from Spring Boot 3.1.6 to 3.2.1 will suddenly strip security with default/no configuration, at least for webflux. * Besides being a good idea or not, this is not mentioned in the release notes either

Comment From: wilkinsona

Thanks, @tgeens. The difference in behaviour between MVC and WebFlux is unintended. It may be that our hands are tied as there are some significant differences in how Spring Security is configured for servlet vs reactive apps. We'll take a look.

Comment From: wilkinsona

As I suspected, there's a significant difference in how Spring Security's reactive implementation behaves that limits our options. With a Servlet-based app, we configure a default SecurityFilterChain if neither the user nor any other auto-configuration has provided one:

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
    http.formLogin(withDefaults());
    http.httpBasic(withDefaults());
    return http.build();
}

This works well, despite there being no UserDetailsService.

The equivalent filter configuration in a reactive app is the following:

@Bean
SecurityWebFilterChain defaultSecurityFilterChain(ServerHttpSecurity http) throws Exception {
    http.authorizeExchange((exchange) -> exchange.anyExchange().authenticated());
    http.formLogin(withDefaults());
    http.httpBasic(withDefaults());
    return http.build();
}

Unfortunately, this doesn't work as basic authentication on the reactive side requires an authentication manager:

Caused by: java.lang.IllegalArgumentException: authenticationManager cannot be null
    at org.springframework.util.Assert.notNull(Assert.java:172) ~[spring-core-6.1.2-SNAPSHOT.jar:6.1.2-SNAPSHOT]
    at org.springframework.security.web.server.authentication.AuthenticationWebFilter.<init>(AuthenticationWebFilter.java:94) ~[spring-security-web-6.2.1-SNAPSHOT.jar:6.2.1-SNAPSHOT]
    at org.springframework.security.config.web.server.ServerHttpSecurity$HttpBasicSpec.configure(ServerHttpSecurity.java:2305) ~[spring-security-config-6.2.1-SNAPSHOT.jar:6.2.1-SNAPSHOT]

One option might be for us to provide an empty authentication manager if there's neither a ReactiveAuthenticationManager nor a ReactiveUserDetailsService in the context:

@Bean
ReactiveAuthenticationManager defaultAuthenticationManager() {
    return (authentication) -> Mono.error(new UsernameNotFoundException(authentication.getName()));
}

Comment From: wilkinsona

On the reactive side, the SecurityWebFilterChain doesn't appear to be needed to achieve the same behaviour as the servlet-side – the ReactiveAuthenticationManager that denies access to every Authentication is sufficient.

Comment From: wilkinsona

Closing in favor of the re-opening of #38713. That fix, which has not yet been released, needs to be reconsidered