spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

OAuth2 Authorization Code redirect not working when using webflux.base-path

Open juliuskrah opened this issue 4 years ago • 8 comments

Describe the bug I am running spring-boot 2.3.1 with spring-boot-starter-oauth2-client, after adding a context-path, everything breaks

To Reproduce I have the following configuration

@Bean
SecurityWebFilterChain securityFilter(ServerHttpSecurity http) {
    var logoutHandler = new OidcClientInitiatedServerLogoutSuccessHandler(repository);
    logoutHandler.setPostLogoutRedirectUri("{baseUrl}");
    var authenticationEntryPoint = new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/keycloak");
    http.authorizeExchange(exchange 
           -> exchange.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() //
        .anyExchange().authenticated()) //
        .logout(logout -> logout.logoutSuccessHandler(logoutHandler) //
        .requiresLogout(new PathPatternParserServerWebExchangeMatcher("/logout"))) //
        .oauth2Login(Customizer.withDefaults()) //
        .oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.jwt())
        .exceptionHandling(exception -> exception.authenticationEntryPoint(authenticationEntryPoint));
    return http.build();
}

And my yaml hass the following

spring:
  webflux:
    base-path: /path  # Breaks once this is added
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://keycloak/auth/realms/app
      client:
        registration:
          keycloak:
            client-id: client
            client-secret: ea898a70-84f2-408f-9d79-ab6e9eab0aa4
        provider:
          keycloak:
            issuer-uri: http://keycloak/auth/realms/app

Looking at the browser network tab, the final request looks like this

Request URL: http://localhost:8082/path/login/oauth2/code/keycloak?state=KCmpLGwuYQnwEc-mufrJ15LJX8D7LVQsesdDOuDO4Aw%3D&session_state=4a79ae1f-d587-4131-b972-9c740e94cd29&code=03ddb325-11ac-48ff-80b4-5fa97dd72e45.4a79ae1f-d587-4131-b972-9c740e94cd29.6145d626-fbb2-4a78-a04b-977fc60223f2
Request Method: GET
Status Code: 302 Found
Remote Address: 127.0.0.1:8082
Referrer Policy: no-referrer-when-downgrade

HTTP/1.1 302 Found
Location:  # location is blank
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
Set-Cookie: SESSION=74b437e4-a788-481c-918e-faab6e3da29d; Path=/path/; HttpOnly; SameSite=Lax
content-length: 0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Cookie: SESSION=055f12b8-3f4c-4be6-95c3-b8126f733086; SESSION=200328fd-c13e-4887-91c1-d6846a5eae92
Host: localhost:8082
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36

Expected behavior Application should redirect me to initially requested page

juliuskrah avatar Aug 17 '20 13:08 juliuskrah

@juliuskrah Did you solve this problem?

ssejinkim avatar Nov 13 '20 07:11 ssejinkim

@codetalkr I took out the context path

juliuskrah avatar Nov 13 '20 09:11 juliuskrah

me too.

ssejinkim avatar Nov 24 '20 10:11 ssejinkim

I added an explicit redirect URI to the client registration:

...

spring:
  webflux:
    base-path: /path
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: my-client
            client-secret: my-secret
            redirect-uri: https://host.domain.com/path/login/oauth2/code/{registrationId}
...

Unfortunately, I'm still having issues with logging out when specifying a base-path.

libantema avatar Mar 11 '21 20:03 libantema

@juliuskrah The reason you are seeing

Location: # location is blank

is because the SPRING_SECURITY_SAVED_REQUEST in the session is blank. The value should be http://localhost:8080/path/. The root of this issue is in RedirectServerAuthenticationSuccessHandler which uses WebSessionServerRequestCache.

webflux-context-path

If you try the oauth2Login Servlet sample, you will see that SavedRequestAwareAuthenticationSuccessHandler obtains a DefaultSavedRequest from HttpSessionRequestCache with the value http://localhost:8080/path/ and it works as expected.

Would you be interested in submitting a PR for this fix?

jgrandja avatar Mar 17 '21 10:03 jgrandja

Hi @jgrandja, you can assign me the task. May I?

parikshitdutta avatar Apr 18 '21 16:04 parikshitdutta

Thank you @parikshitdutta ! The issue is yours.

jgrandja avatar Apr 19 '21 08:04 jgrandja

Is there a workaround for this bug until it is fixed?

The most obvious workaround to me would be to extend RedirectServerAuthenticationSuccessHandler and just override onAuthenticationSuccess() to be something like this?

@Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
	ServerWebExchange exchange = webFilterExchange.getExchange();
	return this.requestCache.getRedirectUri(exchange).defaultIfEmpty(this.location).flatMap((location) -> {
		if (location.toASCIIString().isBlank() && this.location.toASCIIString().isBlank()) {
			location = URI.create("/");
		} else if (location.toASCIIString().isBlank()) {
			location = this.location;
		}
		return this.redirectStrategy.sendRedirect(exchange, location);
	});
}

It seems like a huge pain to just copy an entire class almost word-for-word, so I'm hoping someone else is aware of a quicker/simpler workaround.

libantema avatar Nov 23 '21 22:11 libantema

I ran into the same problem today but what I found out is that it does not work iif the request path equals the base path and does not end in a forward slash. Whether the base path ends in a slash is irrelevant.

So, if you have set spring.webflux.base-path: /path and you request /path, you will end up with /path/login?error and "Invalid credentials" because of the reason described above. However, if you request /path/ or e.g. /path/foo, it works.

As this issue is a bit older and things may have changed in the meantime, here some context about the version, I'm using:

  • Spring Boot: 3.1.5
    • Spring Security: 6.1.5
    • Spring WebFlux: 6.0.13

BrandonSchmitt avatar Nov 06 '23 17:11 BrandonSchmitt