Search code examples
javaspring-bootcurlspring-webfluxbasic-authentication

Can't pass basic authethication via curl


i try to add basic authentication to my Spring Boot non-web application. Tests works fine with @WithMockUser annotation but i cant get proper cURL with correct (as it seems) credentials. My security configuration:

@EnableWebFluxSecurity
public class SecurityConfig {
    @Value("${application.user}")
    private String user;
    @Value("${application.password}")
    private String password;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(
            ServerHttpSecurity http) {
        return http.csrf().disable().authorizeExchange()
                .pathMatchers("/endpoint/v1/**").authenticated()
                .and().build();
    }

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        UserDetails details = User
                .withUsername(user)
                .password(password)
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(details);
    }

}

First attempt was with BCryptPasswordEncoder (and not working) but i get rid of that for simplify the problem.

I use curl like this:

curl -I --user user1:pass localhost:9094/endpoint/v1/health

And i run my application with the same arguments, but still get:

HTTP/1.1 401 Unauthorized
transfer-encoding: chunked
WWW-Authenticate: Basic realm="Realm"
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer

Controller:

@RestController
@RequestMapping("/endpoint/v1")
@Slf4j
public class ApplicationController {

    @GetMapping("/health")
    @Timed
    public Mono<String> health() {
        log.info("Health ok");
        return Mono.just(String.format(STATUS, "ok"));
    }
}

I try postman but same results.

Can anyone tell me what I'm doing wrong here?

@Update

Debug logs from terminal when i try to login with proper credentials:

{"@timestamp":"2021-05-07T09:53:33.488+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] New http connection, requesting read","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.488+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] Initialized pipeline DefaultChannelPipeline{(BootstrapHandlers$BootstrapInitializerHandler#0 = reactor.netty.channel.BootstrapHandlers$BootstrapInitializerHandler), (reactor.left.httpCodec = io.netty.handler.codec.http.HttpServerCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.server.HttpTrafficHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}","logger_name":"reactor.netty.channel.BootstrapHandlers","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.498+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] Increasing pending responses, now 1","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.498+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@7f908d6a","logger_name":"reactor.netty.http.server.HttpServer","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.499+02:00","@version":"1","message":"[c6f8daa9-2] HTTP HEAD \"/endpoint/v1/health\"","logger_name":"org.springframework.web.server.adapter.HttpWebHandlerAdapter","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.500+02:00","@version":"1","message":"Created new WebSession.","logger_name":"org.springframework.web.server.session.DefaultWebSessionManager","thread_name":"boundedElastic-1","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.511+02:00","@version":"1","message":"[c6f8daa9-2] Completed 401 UNAUTHORIZED","logger_name":"org.springframework.web.server.adapter.HttpWebHandlerAdapter","thread_name":"boundedElastic-1","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.512+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] Last HTTP response frame","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"boundedElastic-1","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.512+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] No sendHeaders() called before complete, sending zero-length header","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"boundedElastic-1","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.514+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] Decreasing pending responses, now 0","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}
{"@timestamp":"2021-05-07T09:53:33.514+02:00","@version":"1","message":"[id: 0xc6f8daa9, L:/127.0.0.1:9094 - R:/127.0.0.1:49572] Last HTTP packet was sent, terminating the channel","logger_name":"reactor.netty.http.server.HttpServerOperations","thread_name":"reactor-http-epoll-3","level":"DEBUG","level_value":10000,"application":"myApp"}


Solution

  • When you add spring security to an application you automatically get a default security setup, which is described here in the spring security documentation.

    But as soon as you declare a custom security configuration, then you are basically on your own.

    What is missing in the above configuration is to explicitly configure that you want http basic authentication.

    this is done by adding:

    .httpBasic(withDefaults())
    

    You can read about basic auth here.