Skip to content

Minimal configuration doesn't work with Spring Boot 3.2.0 #1475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Corke123 opened this issue Dec 14, 2023 · 8 comments
Closed

Minimal configuration doesn't work with Spring Boot 3.2.0 #1475

Corke123 opened this issue Dec 14, 2023 · 8 comments
Assignees
Labels
for: external-project For an external project and not something we can fix

Comments

@Corke123
Copy link

Describe the bug

Setting up Spring Authorization Server with minimal configuration as per documentation from here doesn't work with Spring OAuth Client.
The login request fails with message:

No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken

This works fine with Spring Boot version 3.1.3.

Stacktrace:

org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:234) ~[spring-security-core-6.2.0.jar:6.2.0]
	at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:85) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:231) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:131) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.2.0.jar:6.2.0]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

To make it work following configuration is neccessary:

	@Bean
	public DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
		var authProvider = new DaoAuthenticationProvider();
		authProvider.setUserDetailsService(userDetailsService);
		return authProvider;
	}

	@Bean
	public UserDetailsService userDetailsService() {
		return new InMemoryUserDetailsManager(
				User.builder().username("user").password("{bcrypt}$2a$10$fw01ITlZYDiqRC6Z0UCHqujkkbAV4U1w9OPE9nEVJV/NGnyhOMFcW").build()
		);
	}

To Reproduce

Expected behavior
The user successfully logged in.

Sample
A link to a GitHub repository with a minimal, reproducible sample.

@rozagerardo
Copy link
Contributor

rozagerardo commented Dec 19, 2023

An additional note here.

This change in the behavior is explained in the Boot 3.2 Release notes:

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#auto-configured-user-details-service

The auto-configured InMemoryUserDetailsManager now backs off when [...] spring-security-oauth2-resource-server [...] is on the classpath. [...]

If you are using one of the above dependencies yet still require an InMemoryUserDetailsManager [...], define the required bean in your application.

And the spring-security-oauth2-authorization-server has the Spring Security Resource Server library as one of its dependencies, making the form login fail because of this.

So, I now created an issue in the Spring Boot project:

spring-projects/spring-boot#38864

Since I guess it's on their side to decide first if they want to contemplate the Spring Authorization Server minimal config for that feature, or if they want to leave it as it is (and then we'll have to update the instructions, indicating to add a InMemoryUserDetailsManager bean in the context as @Corke123 did above).

@jgrandja
Copy link
Collaborator

Thanks for the details @Corke123 @rozagerardo.

I was able to reproduce the same issue on my end. I'm going to hold off on any changes for now until we see what happens in spring-projects/spring-boot#38864

@rozagerardo
Copy link
Contributor

Quick update on spring-projects/spring-boot#38864 @jgrandja @Corke123 in case you want to have a look at that or comment about the decision:

We think we may be able to improve the situation here by changing the conditions so that the auto-configuration does not back off if you've set spring.security.user.name and/or spring.security.user.password. That won't restore things exactly as they were in 3.1, but I think it's a good compromise. Authorization Server's minimal setup will then work with the small addition of setting spring.security.user.password

@jgrandja
Copy link
Collaborator

Thanks for the update @rozagerardo.

This issue should be resolved when Spring Boot 3.2.2 is released. I'll leave this open for now and confirm the issue has been fixed after 3.2.2 is released.

@rozagerardo
Copy link
Contributor

rozagerardo commented Jan 18, 2024

You're welcome @jgrandja .

One last question here, with the 3.2.2 fix wouldn't we want to add the spring.security.user.name and spring.security.user.password properties to the minimal required configuration section in the docs: https://docs.spring.io/spring-authorization-server/reference/getting-started.html#developing-your-first-application?

Mainly to avoid having users complain that the setup we show is not working out-of-the-box. If they have some basic Spring Security knowledge they will understand the purpose of these properties and what they imply.

@jgrandja
Copy link
Collaborator

jgrandja commented Jan 18, 2024

@rozagerardo

wouldn't we want to add the spring.security.user.name and spring.security.user.password properties to the minimal required configuration section in the docs

Good catch! Yes, we do need to add it. Let me know if you're interested in submitting a PR for this?

rozagerardo added a commit to rozagerardo/spring-authorization-server that referenced this issue Jan 19, 2024
With Boot 3.2.0 the auto-configured InMemoryUserDetailsManager bean (used by our minimal configuration) is backing-off because we include the spring-security-oauth2-resource-server dependency, and with an additional condition introduced since 3.2.2 - if none of the spring.security.user name or password properties is present in the setup.

With this update in the minimal configuration guidelines, the service is starting correctly.

Fixes spring-projectsgh-1475
@rozagerardo
Copy link
Contributor

Sorry for the delay @jgrandja Sure, I now submitted the following PR: #1519

Boot 3.2 Release Notes indicate that just one of the user properties has to be present to prevent backing-off the InMemoryUserDetailsManager bean, but I think it's clearer to include both of them in the guidelines.

I didn't create a draft PR because the Contributor guidelines indicate it is not necessary for minor changes.

And I didn't include any additional note in the docs to keep the changes to a minimum and because we're already indicating "Beyond the Getting Started experience, most users will want to customize the default configuration." and I consider this covers the security user setup as well.

Of course, let me know if anything else is needed here. Have a nice day :)

@jgrandja
Copy link
Collaborator

This is now fixed in Spring Boot 3.2.2. See spring-projects/spring-boot#38864

The samples have been updated to 3.2.2 in gh-1528 and have confirmed the samples are working correctly.

@jgrandja jgrandja self-assigned this Jan 30, 2024
@jgrandja jgrandja added for: external-project For an external project and not something we can fix and removed type: bug A general bug labels Jan 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix
Projects
None yet
Development

No branches or pull requests

3 participants