Skip to content

Refresh and Logout

Keycloak manages three related concepts that control how long a user stays logged in:

ConceptTypical defaultWhat it controls
Access token lifespan5 minutesHow long the JWT is valid before the backend rejects it
Refresh token lifespan30 minutes (SSO Session Idle)How long a refresh token can be used to get new access tokens
SSO session max10 hoursAbsolute maximum session length, even with active refresh

When the access token expires, the frontend should use the refresh token to silently get a new one — without forcing the user to log in again. When the SSO session expires, the user must re-authenticate.

keycloak.updateToken(minValidity) is the idiomatic way to handle refresh in keycloak-js. Call it before any API request; if the current access token will expire within minValidity seconds, keycloak-js silently exchanges the refresh token for a new access token:

// Refresh the token if it expires within 60 seconds,
// then call the API with the fresh token.
async function callApi(path) {
  try {
    await keycloak.updateToken(60);
  } catch {
    // Refresh token has expired — force re-login.
    keycloak.login();
    return;
  }

  const response = await fetch(`https://api.example.com${path}`, {
    headers: { Authorization: `Bearer ${keycloak.token}` },
  });
  return response.json();
}

If updateToken rejects (the refresh token is expired or revoked), the user’s session is gone and you should redirect them to login with keycloak.login().

Calling a local “clear state” function in your app is not enough. The user’s SSO session still exists in Keycloak, which means they could immediately access other apps in the same realm without re-authenticating — or even return to your app and bypass the login screen.

A proper logout hits Keycloak’s end-session endpoint. keycloak-js does this for you:

// Redirect to Keycloak's end-session endpoint,
// then back to your app's home page.
keycloak.logout({ redirectUri: 'https://app.example.com/' });

Under the hood this redirects the browser to:

/realms/{realm}/protocol/openid-connect/logout
?id_token_hint=<id_token>
&post_logout_redirect_uri=https://app.example.com/

Keycloak clears the SSO session cookie, marks the refresh token as revoked, and redirects back to your app.

ActionClears local tokenClears Keycloak session
Local state reset onlyYesNo
keycloak.logout()YesYes
OptionBenefitCost
Silent refresh via updateToken()The user is never interrupted; the session continues as long as the refresh token hasn’t expiredRequires complete error handling, because a silent refresh failure can leave the app looking logged in while every API call fails
Force a re-login immediately when the access token expiresSimpler to implement, no complex refresh flow to manageMuch worse UX — users must re-enter their password often, even though the SSO session itself hasn’t actually expired
Set a short SSO Session Idle/MaxReduces the risk window if a token or session leaksUsers must authenticate again more often, hurting UX especially for apps used continuously for long periods
  • Not handling the case where updateToken() rejects — if the refresh token has expired or been revoked and the app doesn’t catch the error, the user keeps seeing failed API requests with no obvious cause. Always catch it and call keycloak.login() to redirect back to authentication.
  • Logging out by only clearing local state, without calling the end-session endpoint — removing tokens from memory alone doesn’t clear Keycloak’s SSO session cookie, so the user is “logged out” of your app but still logged in realm-wide.
  • Retrying refresh indefinitely on a transient network failure — not distinguishing a network error from a genuinely expired refresh token can make the app loop calling updateToken() repeatedly without ever falling back to the login page.

Real-world example

The keycloak-js updateToken pattern — many production apps call updateToken(minValidity) silently before every API call, exactly as this lesson shows, and catch the rejection to redirect to login only when the refresh token has genuinely expired, instead of letting requests fail silently.

Google Workspace / Gmail on the web — uses the same idea: a long-lived session that refreshes automatically in the background, but with an absolute session timeout (similar to Keycloak’s SSO session max) that forces re-authentication periodically even while silent refresh keeps “succeeding.”

What does keycloak.updateToken(60) do?
What should your app do when keycloak.updateToken() rejects (throws)?
Why is clearing local app state alone insufficient for logout?
What does the SSO Session Idle timeout control?