Describe the bug Configuring OidcClientInitiatedServerLogoutSuccessHandler as a logoutSuccessHandler and WebSessionServerLogoutHandler + SecurityContextServerLogoutHandler as logoutHandlers is not working. With default logoutHandlers everything works fine but sessions won't be invalidated on gateway, only on Keycloak.
To Reproduce Spring Boot 2.7.0 with following dependencies: Spring Cloud Gateway Spring OAuth2 Client Spring Session Data Redis
I'm working on BFF setup and I configured Spring Cloud Gateway as OAuth2 client:
application.yaml:
security:
oauth2:
client:
provider:
keycloak:
issuer-uri: http://localhost:8090/realms/example-realm
registration:
keycloak-client:
provider: keycloak
scope: openid
client-id: example-client
client-secret: example-secret
authorization-grant-type: authorization_code
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
final ReactiveClientRegistrationRepository clientRegistrationRepository;
@Value("${server.url}")
final String serverUrl;
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(
new WebSessionServerLogoutHandler(), new SecurityContextServerLogoutHandler()
);
http.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2Login()
.authenticationFailureHandler(oidcAuthenticationFailureHandler())
.authorizedClientRepository(authorizedClientRepository())
.and()
.logout()
.logoutHandler(logoutHandler)
.logoutSuccessHandler(oidcLogoutSuccessHandler());
return http.build();
}
@Bean
public ServerLogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedServerLogoutSuccessHandler successHandler = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository);
successHandler.setPostLogoutRedirectUri(serverUrl);
return successHandler;
}
@Bean
public ServerAuthenticationFailureHandler oidcAuthenticationFailureHandler() {
return new RedirectServerAuthenticationFailureHandler(serverUrl);
}
/**
* https://github.com/spring-projects/spring-security/issues/7889
*/
@Bean
public ServerOAuth2AuthorizedClientRepository authorizedClientRepository() {
return new WebSessionServerOAuth2AuthorizedClientRepository();
}
Expected behavior When I browse to http://localhost:8085/logout the default logout page shows. I press the log out button but the logout flow fails. The sessions on gateway and keycloak are not invalidated and I receive an 500 error.
2022-05-22 20:36:30.962 ERROR [TraceId=,SpanId=] 34225 --- [ioEventLoop-5-1] a.w.r.e.AbstractErrorWebExceptionHandler : [8a2d7a53-6] 500 Server Error for HTTP POST "/logout"
java.lang.IllegalStateException: Session was invalidated
at org.springframework.session.data.redis.ReactiveRedisSessionRepository.lambda$save$1(ReactiveRedisSessionRepository.java:128) ~[spring-session-data-redis-2.6.3.jar:2.6.3]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
Comment From: jzheaux
Thanks for the report, @adamalexandru4. To facilitate quicker resolution, are you able to publish a minimal GitHub sample?
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: adamalexandru4
Sorry for being late with a response. Here is the sample:
https://github.com/adamalexandru4/gateway-session-bug
Comment From: jgrandja
@adamalexandru4 Thanks for putting the sample together. I took a look at it and discovered that the issue is related to the Spring Session configuration. If you remove SessionConfig or disable Spring Session, the logout works as expected as it logs out of gateway and keycloak.
Unfortunately, I'm not familiar with Spring Session but maybe @rwinch can help out.
Comment From: jgrandja
@adamalexandru4 Just to expand on my previous comment, the issue is not related to OidcClientInitiatedServerLogoutSuccessHandler but directly related to WebSessionServerLogoutHandler and the Spring Session integration.
Comment From: rwinch
It looks like this is due to incorrect ordering when defining DelegatingServerLogoutHandler. As the configuration is currently written, it will first invalid the session and then try to remove the SecurityContext from the already invalidated session. You probably want to stick with the default by removing the explicit configuration on logoutHandler or change the order of the delegates in the constructor of DelegatingServerLogoutHandler.
Comment From: HJK181
@adamalexandru4 have you been able to solve the problem? I'm getting the exact same error and can't figure out how to solve it, already tried to change the order in the DelegatingServerLogoutHandler as suggested by rwinch, without any success...
Comment From: johnnywalker
@rwinch @adamalexandru4 I think there may be an outstanding issue. ServerHttpSecurity.CsrfSpec appends a logout handler which may update the previously invalidated state:
https://github.com/spring-projects/spring-security/blob/9cb668aec2ad14f91c122c66b7d7d4a8b6e133f7/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java#L1902