Learn Keycloak - Refresh Tokens & Token Management
Episode 7 of 31

Learn Keycloak - Refresh Tokens & Token Management

Managing the token lifecycle in Keycloak: understanding the role of the refresh token, rotation and offline access mechanisms, token and session lifetime configuration, and the security steps for detecting token theft.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 6 you used client credentials for machine-to-machine — the client simply requests a new token when the old one expires. Episode 7 is different: for users, forcing a re-login every few minutes obviously doesn't make sense. This is where the refresh token comes in — a sort of rechargeable card that allows a new access token to be obtained without the user typing their password again.

What Is a Refresh Token

Two tokens circulate during a user's login session:

  • Access token — short-lived, sent to the API, contains claims.
  • Refresh token — longer-lived, never sent to an API, only exchanged at the token endpoint.

This division of roles reduces risk: if an access token leaks, the danger window only lasts its short lifetime. If a refresh token leaks, that's a big problem — because it can be used to mint new access tokens over and over.

Access Token vs Refresh Token

AspectAccess TokenRefresh Token
LifetimeShort, measured in minutesMuch longer
ContentClaims and rolesSession identifier
UsageSent to the target APIOnly to the token endpoint
ValidationResource serverKeycloak via the token endpoint
If stolenDangerous within its lifetimeVery dangerous, must be rotated

Remember one golden rule: the refresh token never leaves the application. It stays at the client and doesn't travel with any API request.

The Refresh Token Flow

When the access token approaches expiry, the application exchanges the refresh token at the token endpoint:

Exchanging a refresh token
curl -X POST "https://kc.example.com/realms/my-realm/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "client_id=my-app" \
  -d "client_secret=4f9a2c8d..." \
  -d "refresh_token=eyJhbGciOi..."

The response contains a new access token, and with rotation enabled, a new refresh token too:

Refresh token exchange response
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "token_type": "Bearer"
}

grant_type=refresh_token marks this flow. Notice refresh_expires_in — the lifetime limit of the refresh token itself. As long as this value isn't zero, a further exchange remains possible.

Refresh Token Rotation

Since recent Keycloak versions, refresh token rotation is enabled by default: every time you exchange a refresh token, Keycloak issues a new refresh token and invalidates the old one. The same old token is still accepted within a short window to tolerate request races, but outside that window, using an old token is considered a sign of theft.

This is the main defense against the stolen-token scenario: the thief and the victim compete to use the same token, and Keycloak can detect its repeated use. Don't disable rotation without a very strong reason.

Token Introspection and Revocation

For services that can't verify JWT signatures themselves, Keycloak provides introspection — asking Keycloak to assess a token's status:

Token introspection
curl -s -X POST "https://kc.example.com/realms/my-realm/protocol/openid-connect/token/introspect" \
  -H "Authorization: Basic base64(client_id:client_secret)" \
  -d "token=eyJhbGciOi..."

An active: true response means the token is still valid; active: false means it has expired or been revoked. To explicitly revoke a token — e.g. when a user logs out or a refresh token is suspected stolen — use the revoke endpoint:

Revoking a refresh token
curl -X POST "https://kc.example.com/realms/my-realm/protocol/openid-connect/revoke" \
  -d "client_id=my-app" \
  -d "client_secret=4f9a2c8d..." \
  -d "token=eyJhbGciOi..."

Important

Remember the simple rule: access tokens can't be revoked directly. Because they're stateless JWTs, revoking them means waiting for expiry or forcing Keycloak to rotate keys. What can be revoked quickly are refresh tokens and user sessions. That's why you should treat the refresh token as the most sensitive asset in the flow.

Token and Session Configuration

All these numbers are set in Realm settings under the Tokens and Sessions tabs:

  • Access Token Lifespan — the access token lifetime, defaulting to a few minutes.
  • SSO Session Idle — how long a session stays active without activity.
  • SSO Session Max — the absolute cap on a session's life.

These three values determine a token's real lifecycle: even though a refresh token looks long-lived, it dies once its SSO session dies. Adjust to your needs — relaxed internal apps can be long, financial applications should be short.

Offline Access and Remember Me

For cases that need tokens to live longer — e.g. a mobile app opened once a month — there are two features:

  • Offline access — the client requests the offline_access scope, and Keycloak issues an offline refresh token that stays valid even after the user's SSO session expires. Its lifetime is controlled by Offline Session Idle and Offline Session Max.
  • Remember me — the user ticks Remember me on the login screen, and their session lasts longer, governed by the Remember Me value in the session settings.

The difference: remember me stays tied to the user's session, while offline access lives independently. Choose deliberately — both extend credential exposure.

Token Security

Several layers of defense for tokens:

  • Rotation and reuse detection — enable refresh token reuse detection and revoke the session immediately when detected.
  • Token binding — bind the token to its origin context. With MTLS, the access token points to a specific client certificate so a stolen token can't be used from another device.
  • Anomaly detection — monitor patterns: refreshing from a different IP or device within a short time is a danger signal.
  • Token audit — enable Admin events and User events in the realm, then regularly review login, refresh, and revoke event logs.

A practical addition: token caching on the resource server side. Don't re-validate the signature on every request when you don't have to — cache the validation result until exp, with exceptions for actively revoked tokens.

Closing

Episode 7 explored refresh tokens: their role in extending access without re-login, the exchange flow, rotation and reuse detection, introspection and revocation, lifespan configuration in the realm, offline access and remember me, and token security strategies.

Key takeaways:

  • Short access tokens, long refresh tokens — never send a refresh token to an API.
  • Refresh token rotation is the first fence — enable it and monitor reuse detection.
  • All tokens die when the SSO session dies — SSO Session Idle and Max are the real limits.
  • Offline access and remember me extend risk — use them only when truly needed.

In the next episode (episode 8), you'll learn about scopes and consent — the mechanism that determines what data an application may take from a user's account, and how Keycloak presents that choice to the user on the consent screen.

Learn Keycloak - Refresh Tokens & Token Management | Learn SSO with Keycloak