I have a Spring Boot application that is configured as a Spring Security OAuth2 Client. I use Keycloak as my OIDC provider. I serve a compiled Angular application from my Spring Boot application. The Angular app keeps making requests every second to a REST API in the Spring Boot Application.

When the user opens this application in the browser at localhost:4881, they're redirected to the Keycloak login page and after a successful login, they're redirected back to localhost:4881 where the compiled Angular app is rendered.

When a user logs in, I see two sessions created in Keycloak, one called Regular SSO and the other Offline. The Regular SSO session gets cleared out as per the SSO Idle Timout. However, the Offline session remains for a longer duration in Keycloak but gets cleared from Keycloak eventually.

I am unable to redirect the UI to the Keycloak login page after the SSO Session Idle/ SSO Session Max timeout. My assumption is that Keycloak should communicate this session timeout event to the OAuth2 Client and the OAuth2 Client should redirect the UI to the Keycloak Login page for all following requests.

Can someone please tell me if I'm missing something here. I was unsuccessful in finding relevant documentation for this problem.

I set the SSO Session Idle, SSO Session Max and the Offline Session Settings at the realm level. I've set the Admin URL in the client as well.

Realm Settings

Client Settings

I have created a sample application with the exact same setup here: https://github.com/sakethsusarla/keycloak-oauth2-client-ui

backend: A Spring Boot Application configured as an OAuth2 Client, returns the current time through a REST API exposed at /api/currentTime

enter image description here

frontend: An Angular App that fetches current time from the backend

enter image description here

realm: The realm configuration is present in "keycloak-oauth2-client-ui\backend\src\main\resources\demo-realm.json"

The username and password to login are demo and demo

1

There are 1 best solutions below

1
On

After reading that Keycloak doesn't initiate a Backchannel Logout if a session expires, I decided to add a filter to check the validity of the token and invalidate the session if required.

Adding the following filter worked for me. I've updated my repository with the changes in the main branch.

@Component
public class TokenExpirationFilter extends OncePerRequestFilter {

    private final OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository;

    @Autowired
    TokenExpirationFilter(OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository) {
        this.oAuth2AuthorizedClientRepository = oAuth2AuthorizedClientRepository;
    }

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication instanceof OAuth2AuthenticationToken token) {
            final OAuth2AuthorizedClient client =
                    oAuth2AuthorizedClientRepository.loadAuthorizedClient(
                            token.getAuthorizedClientRegistrationId(),
                            authentication,
                            request);

            final OAuth2AccessToken accessToken = client.getAccessToken();

            if (accessToken.getExpiresAt() != null && accessToken.getExpiresAt().isBefore(Instant.now())) {
                SecurityContextHolder.getContext().setAuthentication(null);
                SecurityContextHolder.clearContext();

                final HttpSession httpSession = request.getSession();

                if (httpSession != null) {
                    httpSession.invalidate();
                }
            }
        }

        filterChain.doFilter(request, response);
    }