7

Let me shortly describe what problem i am facing right now.

I have configured spring security for webflux application, and i am getting login form prompted, when i try to access the route that doesn't require authentication. The route is /swagger-ui/ and it should get opened without any login forms or whatever.

Below is the code i have within the SecurityWebFilterChain


@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    //@formatter:off
    return http
            .formLogin().disable()
            .httpBasic().disable()
            .authenticationManager(authenticationManager)
            .securityContextRepository(securityContextRepository)
            .authorizeExchange()
            .pathMatchers(HttpMethod.OPTIONS).permitAll()
            .pathMatchers("/v2/api-docs", "/v3/api-docs", "/configuration/ui", "/swagger-resources",
                    "/configuration/security", "/swagger-ui/", "/swagge‌​r-ui",
                    "/webjars/**", "/swagger-resources/configuration/ui",
                    "/swagger-resources/configuration/security").permitAll()  // Allowed routes for swagger
            .pathMatchers("/api/auth", "/api/auth/**").permitAll() // Allowed routes for auth
            .and()
            .authorizeExchange()
            .anyExchange()
            .authenticated() // All other routes require authentication
            .and()
            .csrf().disable()
            .headers()
            .hsts()
            .includeSubdomains(true)
            .maxAge(Duration.ofSeconds(31536000))
            .and()
            .frameOptions().mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)
            .and()
            .build();
    //@formatter:on
    }
}

If anyone has any suggestions, please let me know, i will appreciate it. Here is the picture what i got in the browser.

enter image description here

saif
  • 1,182
  • 14
  • 27
Milos
  • 394
  • 2
  • 19
  • Are you sure it is even loaded, and what does your debug output tell you, and the browser logs – Toerktumlare Aug 27 '20 at 11:45
  • @ThomasAndolf well browser console logs are empty, i will try to debug it now. – Milos Aug 27 '20 at 11:52
  • But the browser request log, what requests are performed and what requests are red, return statuses, etc – Toerktumlare Aug 27 '20 at 12:18
  • That diolog box looks like ”basic auth” and is given by the webbrowser and not spring. The path ”/” seems to still have basic auth and someone is requesting that path – Toerktumlare Aug 27 '20 at 12:24
  • 1
    @ThomasAndolf I have fixed it accidentally, .httpBasic() .and() .formLogin().disable() i have removed .disable() from the httpBasic() and apparently its working, but let me chek once again if i have modified something else also. – Milos Aug 27 '20 at 13:26
  • @ThomasAndolf yeah well besides removing disable(), there is one more thing that i have done and that is in authenticate method which is overriden method from ReactiveAuthenticantionManager. Well in that method, i have created UsernamePasswordAuthenticationToken with hardcoded values like principal, authorities etc. – Milos Aug 27 '20 at 14:35

2 Answers2

11

I was really annoyed by this issue too. The problem is that by putting .httpBasic().disable() in your code you would expect spring to skip basic authentication (that browser window) but it doesn't.

Instead, try providing a ServerAuthenticationEntryPoint to the .httpBasic().

The most simple one is the HttpStatusServerEntryPoint.

For example in your code change to:

***
return http
            .formLogin().disable()
            .httpBasic().authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
            .authenticationManager(authenticationManager)
            ***

By changing that your server will return a 401 UNAUTHORIZED HttpStatus instead of that browser window! Cheers!

Michail Angelos
  • 121
  • 2
  • 6
0

For those who would like to keep .httpBasic().disable() in their code while making the basic login go away I have the following setup that works.

The issue lies in that a default HttpBasicServerAuthenticationEntryPoint is configured as the fallback if there isn't another ServerAuthenticationEntryPoint. This entry point returns WWW-Authenticate header with value Basic realm="realm" which prompt the browser to pop the login prompt.

To this disable the entrypoint we cannot just disable httpBasic, but we must configure a custom ServerEntryPoint. I did it through a authentication filter but .exceptionHandling().authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)).and() should also work.

My setup (which use authentication filter):

http.
        .httpBasic().disable() // Disable Basic Login
        .formLogin().disable() // Disables Form Login
        .logout().disable() // Disables default Logout endpoint
        .authenticationManager(authManager)
        .addFilterAt(createCustomJwtFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
        .authorizeExchange()
            .pathMatchers(HttpMethod.OPTIONS).permitAll()
            .pathMatchers("/", "/healthcheck").permitAll()
            .anyExchange().authenticated();
return http.build();

createCustomJwtFilter() method

private static AuthenticationWebFilter createCustomJwtFilter(ReactiveAuthenticationManager authManager)
{
    AuthenticationWebFilter filter = new AuthenticationWebFilter(authManager);
    // This line configures the custom entryPoint
    filter.setAuthenticationFailureHandler(
            new ServerAuthenticationEntryPointFailureHandler(HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)));
    return filter;
}

This should also return a 401, while keeping the httpBasic() disabled for better code readability.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
HarryH
  • 3
  • 4