Description
Describe the bug
Since migrating to Spring Security 6, Calling APIs using simple jQuery/XHR with basic auth results in final 401 errors, despite being logged in through the browser basic auth dialog.
Analyzing the responses, they are missing the mandatory WWW-Authenticate
header. Thus the browser will not attempt the (already present) basic auth credentials.
IMHO this is a bug, as that header is mandatory: https://datatracker.ietf.org/doc/html/rfc9110#name-www-authenticate
To Reproduce
Implement a simple app with
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.cors(cors -> cors.disable())
.authorizeHttpRequests(
matchers -> matchers
.requestMatchers("/some/**", "/some/more/**", "/error/**")
.permitAll()
.requestMatchers("/api/**", "/app/**")
.authenticated()
.anyRequest().authenticated())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(basic -> basic.realmName("my-realm"))
.build();
}
// a valid AuthenticationProvider, too...
Expected behavior
have a (valid) www-authenticate
response header in all 401 responses.
Additional Info
The behaviour was first implemented here: 4ef0460#diff-0f2a9f7a8a020191e00efb336582d7d71dd46130bc4b5cbc86eba681c498751fR92
Current state:
I guess this line of code just does not explicitly put the www-authenticate
header.
Workaround
My workaround is to provide a simple BasicAuthenticationEntryPoint without that special treatment for XMLHttpRequest
:
BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthEntryPoint.setRealmName(BASIC_AUTH_REALM);
and setting it in the SecurityFilterChain
.httpBasic(basic -> basic.realmName(BASIC_AUTH_REALM).authenticationEntryPoint(basicAuthEntryPoint))