Describe the bug We are using Spring Security SAML2 SSO and SLO.

If I execute single logout from any other app or the IDP when my apps session is expired, we are redirected to apps logout page (or error page if logout page is turned off) in app instead of continuing single logout chain.

To Reproduce Any of the Sample apps with an IDP - Spring SAML2 Sample App

Either wait for session to timeout or just delete session cookie and try to logout from an external location like IDP.

Expected behavior When single logout is executed outside of my app and the IDP sends its logout request to Spring, it would respond with successful because the session was already expired and cannot be found for that user. Otherwise the single logout chain will be broken.

Maybe that would not be the flow, but I would expect the single logout chain to finish.

Comment From: jzheaux

Thanks for the report, @mmoussa-mapfre, we'll see about getting this resolved in the next release.

As a workaround, you can include the {registrationId} in your logout request URL. This will allow the application to look up the appropriate RelyingPartyRegistration from the URL instead of trying to infer it from the logged-in user.

For example, you can do:

http
    .saml2Logout((saml2) -> saml2
        .logoutRequest((request) -> request
            .logoutUrl("/logout/saml2/slo/{registrationId}")
        )
    )
    // ...

Another way is to register the Saml2LogoutRequestFilter directly yourself and provide a custom RelyingPartyRegistrationResolver that selects the appropriate registration. If you have only one registration, it could look like this:

@Bean 
SecurityFilterChain app(HttpSecurity http, RelyingPartyRegistrationRepository registrations) {
    Saml2LogoutRequestFilter logoutRequestFilter = logoutRequestFilter(registrations);
    http
        // ...
        .saml2Logout((saml2) -> saml2
            .withObjectPostProcessor(new ObjectPostProcessor<Saml2LogoutRequestFilter>() {
                @Override 
                public Saml2LogoutRequestFilter postProcess(Saml2LogoutRequestFilter filter) {
                        return logoutRequestFilter;
                }
            })
        )
        // ...

    return http.build();
}

private Saml2LogoutRequestFilter logoutRequestFilter(RelyingPartyRegistrationRepository registrations) {
    RelyingPartyRegistrationResolver resolver = (request, id) -> 
            registrations.findByRegistrationId("my-only-registration-id");
    return new Saml2LogoutRequestFilter(
            resolver,
            new OpenSamlLogoutRequestValidator(),
            new OpenSaml4LogoutResponseResolver(resolver),
            new SecurityContextLogoutHandler());
}

Comment From: mmoussa-mapfre

@jzheaux I was able to use your first solution. Logout returns successful even when the session is expired now. Thank you!

http
    .saml2Logout((saml2) -> saml2
        .logoutRequest((request) -> request
            .logoutUrl("/logout/saml2/slo/{registrationId}")
        )
    )
    // ...