-1

I am currently using a rather simple approach to restrict a certain suburl (everything under /api/rest) and all of its subpaths via WebFluxSecurity. Some paths (everything directly under the root NOT in /api/rest) are excluded so that they can be access without authorization. However, sometimes the accessing party might send an empty authorization header which leads to unsecured endpoints returning a 401.

See the relevant code here:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Value(value = "${...}")
    private String user;
    @Value(value = "${...}")
    private String pw;

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        UserDetails user = User
                .withUsername(user)
                .password(encoder.encode(pw))
                .roles("USER")
                .build();

        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/api/rest/**")
                        .authenticated()
                        .anyExchange()
                        .permitAll()
                )
                .httpBasic(withDefaults());
        return http.build();
    }
}

On stackoverflow I've only found a few suggestions how to handle this with WebSecurity. However, this is not possible for me as I use webflux security.

See e.g.

Springboot webflux throwing 401 when Authorization header sent to unrestricted endpoint

Spring Boot 2: Basic Http Auth causes unprotected endpoints to respond with 401 "Unauthorized" if Authorization header is attached

jju-untis
  • 3
  • 1
  • You mention passing an empty `Authorization` header. I cannot reproduce the behavior you're describing, and reviewing the code for `ServerHttpBasicAuthenticationConverter` which reads the header, it does not appear like an empty header value would cause a 401. Can you provide a sample request that reproduces this issue? – Steve Riesenberg Feb 03 '23 at 15:48
  • Maybe easier to replicate: it is not just an empty auth header that creates a 401. Even calling the endpoints that should match with `.anyExchange().permitAll()` (so unprotected endpoints) with wrong authentication (wrong password e.g) results in a 401. As far as I understand this is the case because of the `.authorizeExchange` that performs a match against the ReactiveUserDetails. – jju-untis Feb 06 '23 at 08:42

1 Answers1

0

TL;DR

If you pass invalid credentials to any endpoint with httpBasic() enabled, it will return a 401 response.

One important distinction that's relevant here is the difference between authentication and authorization. The httpBasic() DSL method adds the AuthenticationWebFilter configured for HTTP Basic. The authorizeExchange(...) DSL method defines authorization rules, such as authenticated() and permitAll().

The authentication filter appears earlier in the Spring Security filter chain than the authorization filter, and so authentication happens first which we would expect. Based on your comments, it seems you are expecting authentication not to happen if you mark an endpoint as permitAll(), but this is not the case.

Whether authentication is actually attempted against a particular request depends on how the authentication filter matches the request. In the case of AuthenticationWebFilter, a ServerWebExchangeMatcher (requiresAuthenticationMatcher) determines whether authentication is required. For httpBasic(), every request requires authentication. If you pass invalid credentials to any endpoint with httpBasic() enabled, it will return a 401 response.

Additionally, a ServerAuthenticationConverter (authenticationConverter) is used to read the Authorization header and parse the credentials. This is what would fail if an invalid token (or Authorization header) is given. ServerHttpBasicAuthenticationConverter is used for httpBasic() and is fairly forgiving of invalid header values. I don't find any scenarios that fail and produce a 401 response except invalid credentials.

Steve Riesenberg
  • 4,271
  • 1
  • 4
  • 26