spring-security
spring-security copied to clipboard
OAuth2 Authorization Code redirect not working when using webflux.base-path
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 Did you solve this problem?
@codetalkr I took out the context path
me too.
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
.
@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
.
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?
Hi @jgrandja, you can assign me the task. May I?
Thank you @parikshitdutta ! The issue is yours.
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.
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