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