Skip to content

Single sign-off: back-channel support #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
heruan opened this issue Aug 31, 2022 · 2 comments
Closed

Single sign-off: back-channel support #29

heruan opened this issue Aug 31, 2022 · 2 comments
Assignees

Comments

@heruan
Copy link
Member

heruan commented Aug 31, 2022

Split of #24 to track back-channel logout support.

@heruan
Copy link
Member Author

heruan commented Aug 31, 2022

To support back-channel logout, we can set a request filter matching the configured route for that purpose and invalidating the session identified by the request. The filter may look like this:

public class BackChannelLogoutFilter extends GenericFilterBean {

    private RequestMatcher requestMatcher;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (requestMatcher.matches(request)) {
           // 1. Get the token
           // 2. Validate the token
           // 3. Get the Session-ID claim
           // 4. Invalidate the corresponding session
        }
        chain.doFilter(request, response);
    }

    public void setRequestMatcher(RequestMatcher logoutRequestMatcher) {
        requestMatcher = logoutRequestMatcher;
    }
}

@heruan
Copy link
Member Author

heruan commented Sep 1, 2022

The token sent by the provider contains the sid claim, i.e. the OIDC Session-ID that has been terminated. We have to find and terminate the corresponding (active) sessions, but to do so we need to get all the current servlet sessions and match the ones to terminate.

Spring has a SessionRegistry bean with information about the active sessions and an API to flag each as due to expire the next time it's accessed: SessionInformation::expireNow.

We can use this registry to get all the principal matching the sid claim and force their sessions to expire:

final var sid = token.getClaimAsString("sid");
sessionRegistry.getAllPrincipals().stream().filter(principal -> {
    if (principal instanceof OidcUser) {
        final var user = (OidcUser) principal;
        return sid.equals(user.getClaimAsString("sid"));
    } else {
        return false;
    }
}).flatMap(p -> sessionRegistry.getAllSessions(p, false).stream()).forEach(SessionInformation::expireNow);

This should trigger an authentication exception the next time those sessions are accessed, so we might want to add an exception handler for that to redirect the browser to the login route:

http.exceptionHandling(exceptionHandling -> {
    var entryPoint = new LoginUrlAuthenticationEntryPoint(loginRoute); // from configuration     
    exceptionHandling.authenticationEntryPoint(entryPoint);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant