Describe the bug If both a HTTP basic authentication and a form login are defined, unauthenticated requests are redirected to the form login page, even if they should be handled by HTTP basic.
To Reproduce
Configure two security filter chains as explained here (/api with HTTP basic for user role API and other request mapping for user role WEB). All unauthenticated requests to /api/entity are redirected to the login page.
Expected behavior
The definition of a second security filter chain with lower priority should not impact the configuration for /api/** endpoints. As written in the docs:
In the Multiple SecurityFilterChain figure, FilterChainProxy decides which SecurityFilterChain should be used. Only the first SecurityFilterChain that matches is invoked. If a URL of
/api/messages/is requested, it first matches on the SecurityFilterChain0 pattern of/api/**, so only SecurityFilterChain0 is invoked, even though it also matches on SecurityFilterChainn. If a URL of /messages/ is requested, it does not match on the SecurityFilterChain0 pattern of/api/**, so FilterChainProxy continues trying each SecurityFilterChain. Assuming that no other SecurityFilterChain instances match, SecurityFilterChainn is invoked.
So in the example, the status code 401 should be sent because /api is handled by HTTP basic.
Sample https://github.com/l-uuz/spring-mix-authentication-bug
Also, if you login as apiuser via the login form, you can access api/entity in the browser. For all other endpoints you get empty body, not even the Whitelabel Error Page. I suspect that this should not be so.
Comment From: marcusdacoregio
Hi @l-uuz, thanks for reaching out.
Spring Security 6 changed its default and now it applies to every dispatcher type, this means that the ERROR dispatcher will also be considered for authorization rules.
With that said, when there's an error in a Spring Boot application, the server redirects the request to the /error page. Because the /error path does not start with /api/**, it will fall under the form login SecurityFilterChain, therefore redirecting you again to /login since the /error page won't be authorized because any request requires authentication.
You can create another SecurityFilterChain to have exclusive rules for the /error page, like so:
@Bean
@Order(1)
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests((requests) -> requests
.anyRequest().hasRole("ADMIN")
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain errorFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/error")
.authorizeHttpRequests((requests) -> requests
.anyRequest().permitAll()
);
return http.build();
}
@Bean
@Order(3)
public SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
Also, if you login as apiuser via the login form, you can access api/entity in the browser. For all other endpoints you get empty body, not even the Whitelabel Error Page. I suspect that this should not be so.
I tried this scenario here but I received a 403 if trying to access any endpoint starting with /api/**.