While looking at the reactive side of #35338 I have learned that an app that depends on spring-boot-starter-security and spring-boot-starter-webflux will fail to start if ReactiveUserDetailsServiceAutoConfiguration is excluded:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'httpHandler' defined in class path resource [org/springframework/boot/autoconfigure/web/reactive/HttpHandlerAutoConfiguration$AnnotationConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.http.server.reactive.HttpHandler]: Factory method 'httpHandler' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.WebFilterChainFilter' defined in class path resource [org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.server.WebFilterChainProxy]: Factory method 'springSecurityWebFilterChainFilter' threw exception; nested exception is java.lang.IllegalArgumentException: authenticationManager cannot be null
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:633) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921) ~[spring-context-5.3.30.jar:5.3.30]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.30.jar:5.3.30]
        at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[spring-boot-2.7.16.jar:2.7.16]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.16.jar:2.7.16]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.16.jar:2.7.16]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.16.jar:2.7.16]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.16.jar:2.7.16]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.16.jar:2.7.16]
        at com.example.reactivesecurityproblem.ReactiveSecurityProblemApplication.main(ReactiveSecurityProblemApplication.java:10) ~[main/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.http.server.reactive.HttpHandler]: Factory method 'httpHandler' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.WebFilterChainFilter' defined in class path resource [org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.server.WebFilterChainProxy]: Factory method 'springSecurityWebFilterChainFilter' threw exception; nested exception is java.lang.IllegalArgumentException: authenticationManager cannot be null
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[spring-beans-5.3.30.jar:5.3.30]
        ... 19 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.WebFilterChainFilter' defined in class path resource [org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.server.WebFilterChainProxy]: Factory method 'springSecurityWebFilterChainFilter' threw exception; nested exception is java.lang.IllegalArgumentException: authenticationManager cannot be null
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:481) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory$1.orderedStream(DefaultListableBeanFactory.java:481) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.web.server.adapter.WebHttpHandlerBuilder.applicationContext(WebHttpHandlerBuilder.java:172) ~[spring-web-5.3.30.jar:5.3.30]
        at org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration$AnnotationConfig.httpHandler(HttpHandlerAutoConfiguration.java:64) ~[spring-boot-autoconfigure-2.7.16.jar:2.7.16]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.30.jar:5.3.30]
        ... 20 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.server.WebFilterChainProxy]: Factory method 'springSecurityWebFilterChainFilter' threw exception; nested exception is java.lang.IllegalArgumentException: authenticationManager cannot be null
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.30.jar:5.3.30]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[spring-beans-5.3.30.jar:5.3.30]
        ... 37 common frames omitted
Caused by: java.lang.IllegalArgumentException: authenticationManager cannot be null
        at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.30.jar:5.3.30]
        at org.springframework.security.web.server.authentication.AuthenticationWebFilter.<init>(AuthenticationWebFilter.java:94) ~[spring-security-web-5.7.11.jar:5.7.11]
        at org.springframework.security.config.web.server.ServerHttpSecurity$HttpBasicSpec.configure(ServerHttpSecurity.java:2117) ~[spring-security-config-5.7.11.jar:5.7.11]
        at org.springframework.security.config.web.server.ServerHttpSecurity.build(ServerHttpSecurity.java:1409) ~[spring-security-config-5.7.11.jar:5.7.11]
        at org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.springSecurityFilterChain(WebFluxSecurityConfiguration.java:109) ~[spring-security-config-5.7.11.jar:5.7.11]
        at org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.springSecurityFilterChain(WebFluxSecurityConfiguration.java:92) ~[spring-security-config-5.7.11.jar:5.7.11]
        at org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.getSecurityWebFilterChains(WebFluxSecurityConfiguration.java:85) ~[spring-security-config-5.7.11.jar:5.7.11]
        at org.springframework.security.config.annotation.web.reactive.WebFluxSecurityConfiguration.springSecurityWebFilterChainFilter(WebFluxSecurityConfiguration.java:69) ~[spring-security-config-5.7.11.jar:5.7.11]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.30.jar:5.3.30]
        ... 38 common frames omitted

The problem does not occur if ReactiveSecurityAutoConfiguration is also excluded. Perhaps ReactiveSecurityAutoConfiguration needs to back off in the absence of an AuthenticationManager bean but we'll need to be careful about unintended side-effects.

Comment From: wilkinsona

needs to back off in the absence of an AuthenticationManager bean

It's more complex than this. Spring Security can either use an AuthenticationManager bean directly or it can use a ReactiveUserDetailsService from which it will create a UserDetailsRepositoryReactiveAuthenticationManager.

With this extra complexity, I think there's an increased risk of regression. Given that this hasn't been reported by users, I intend to only fix this in 3.2.x to help with #35338. We can then back port the changes if needed and if they haven't caused any problems.

Comment From: wilkinsona

There's further complexity. Whether or not @EnableWebFluxSecurity requires an AuthenticationManager depends on the security configuration. For example, the original stack trace in this issue shows a failure when trying to use HTTP basic without an authentication manager. If another form of security is being used, there may be no failure.

With a change in place to only enable WebFlux security when there's an AuthenticationManager bean or a UserDetailsRepositoryReactiveAuthenticationManager, ReactiveManagementWebSecurityAutoConfigurationTests.backOffIfReactiveOAuth2ResourceServerAutoConfigurationPresent() fails because there's no springSecurityFilterChain bean.

Comment From: wilkinsona

Re-opening as one of the conditions is wrong. It should be using ReactiveAuthenticationManager but it is using AuthenticationManager instead.

Comment From: wilkinsona

With thanks to @jzheaux, we also need to consider the case where the user's defined a SecurityWebFilterChain. In that case a ReactiveAuthenticationManager bean may not be needed as it could have been configured directly using the DSL.

Comment From: sanderino666

This issue occurred in one of our test classes when upgrading to Spring Boot 3.2.0 (Webflux app). As stated in the release notes you need to define a bean if you need the MapReactiveUserDetailsService (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#auto-configured-user-details-service).

For people searching for a potential solution, adding the snippet below in a test config solved it for us.

@Bean
MapReactiveUserDetailsService userDetailsService() {
  UserDetails userDetails = User.withUsername("admin").password("admin").roles("ADMIN").build();
  return new MapReactiveUserDetailsService(List.of(userDetails));
}

Comment From: NadChel

Came here guided by this comment on TestEnableWebfluxSecurityAutoConfiguration hidden in the depths of spring-cloud-gateway-server's test directory

/*
 See https://github.com/spring-projects/spring-boot/issues/37504.
 Because gateway has normal spring security and spring security oauth 2
 we need an explicit @EnableWebFluxSecurity and a ReactiveUserDetailsService
 */
@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class)
@ConditionalOnClass({ DispatcherHandler.class, MapReactiveUserDetailsService.class })
@EnableWebFluxSecurity
public class TestEnableWebfluxSecurityAutoConfiguration {

Have been painfully debugging for two days trying to figure out why on earth I have a WebFilterChainProxy in my test's context (along with its overprotective filters) even after explicitly excluding ReactiveSecurityAutoConfiguration (couldn't reproduce it elsewhere). I found the culprit!

Are you all absolutely sure we can't get rid of it and solve this issue a better way? I'm sorry I'm not suggesting anything specific, but I'm done with debugging for a while

Comment From: wilkinsona

I suspect that code in Spring Cloud Gateway can be removed or at least refined thanks to https://github.com/spring-projects/spring-boot/issues/38713.