Session Timeouts and Idle Policy
Short session timeouts are a common compliance requirement. PCI DSS v4.0 requirement 8.2.8 asks that a session idle for more than 15 minutes requires the user to re-authenticate. Similar rules appear in HIPAA guidance and many internal security policies.
Skycloak ships a Session & timeout policy recipe that sets the realm side of this in one step. This guide explains what the recipe changes, and the part your application has to do.
Apply the recipe
Go to Recipes and choose Session & timeout policy, or use the Tune action on the Session & tokens row of a realm’s Security posture card.
Pick a starting point:
| Preset | Idle timeout | Session lifetime | Access token |
|---|---|---|---|
| PCI DSS 8.2.8 baseline | 15 minutes | 8 hours | 5 minutes |
| Balanced | 30 minutes | 10 hours | 5 minutes |
Every value is editable before you apply, and the wizard shows you exactly what changes on your realm before anything is written.
If you edit a value so that it no longer meets the PCI baseline, the policy is relabelled Custom. Skycloak will not record a configuration as meeting 8.2.8 when it does not.
The part your application has to do
This is the most important section of this guide.
Keycloak measures session idleness using the time since the session was last refreshed. Refreshing a token counts as activity. If your application runs a background refresh loop, which most OIDC libraries do by default, the idle timer is reset on every refresh cycle.
The practical effect: a user opens your app, walks away, and leaves the tab open. The library keeps refreshing the token every few minutes. The session never goes idle, and it survives well past 15 minutes of the user doing nothing.
The realm setting is necessary but not sufficient. To actually meet an idle-timeout requirement, your application has to track real user activity and stop refreshing when there is none.
The pattern
- Track genuine user interaction:
mousedown,keydown,scroll,touchstart,visibilitychange. - Record the timestamp of the last interaction.
- Before each token refresh, check how long it has been. If it exceeds your idle limit, do not refresh. Log the user out instead.
keycloak-js
keycloak-js refreshes on a timer you control, so gate the call:
const IDLE_LIMIT_MS = 15 * 60 * 1000;
let lastActivity = Date.now();
for (const evt of ['mousedown', 'keydown', 'scroll', 'touchstart']) {
window.addEventListener(evt, () => { lastActivity = Date.now(); }, { passive: true });
}
setInterval(() => {
if (Date.now() - lastActivity > IDLE_LIMIT_MS) {
keycloak.logout();
return;
}
keycloak.updateToken(70).catch(() => keycloak.logout());
}, 60_000);oidc-client-ts
Disable the library’s automatic silent renew and drive it yourself:
const userManager = new UserManager({
// ...
automaticSilentRenew: false,
});
setInterval(async () => {
if (Date.now() - lastActivity > IDLE_LIMIT_MS) {
await userManager.signoutRedirect();
return;
}
await userManager.signinSilent();
}, 60_000);NextAuth
Set session.maxAge to your idle limit and refetch only on activity:
// pages/_app.tsx
<SessionProvider session={session} refetchInterval={0}>Then call useSession().update() from your own activity handler rather than on a fixed interval.
Server-rendered and native applications
The same rule applies. Any component holding a refresh token needs to stop using it once the user has been inactive for your limit, and discard it rather than keep refreshing in the background.
What the recipe changes
| Setting | What it controls |
|---|---|
| Idle timeout | How long a session survives without activity |
| Session lifetime | Hard limit on a session regardless of activity |
| Idle timeout and session lifetime (remember me) | The same two limits when a user ticks “Remember me”. Pinned so remember-me cannot extend a session past your policy |
| Client session idle and lifetime | Per-application session limits |
| Access token lifetime | How long an issued access token stays valid |
| Access token lifetime (implicit flow) | The same, for the implicit flow |
| Offline session cap | Bounds how long an offline token can live |
Two settings are deliberately not changed:
- Remember me itself stays under your control on the Branding page. The recipe pins how long a remember-me session may last, but does not remove the checkbox from your login page.
- Offline session idle timeout is left alone. Offline tokens exist for background access such as nightly jobs and mobile applications that check in occasionally. Capping their idle time at 15 minutes would break them, and requirement 8.2.8 is about interactive user sessions.
Optional: refresh token rotation
The wizard offers refresh token rotation as an unchecked option. It is good practice, and it is not required by 8.2.8.
Turn it on only if you know your applications handle it. Any client that replays a refresh token, including multi-tab single-page applications that race a refresh and mobile applications that cache one, will start failing with Invalid refresh token.
Applying signs some users out
Keycloak evaluates these limits when a session is next used. Tightening the session lifetime means sessions already older than the new limit end immediately.
Apply during a maintenance window if that matters to you.
Undo
The recipe records the values it overwrote. Revert to previous values on the recipe page restores them.
If the realm has changed since the recipe was applied, the revert is blocked and you are shown what would be overwritten, so a revert never silently discards a later change of yours.
Drift
Skycloak re-checks these settings and reports the policy as degraded if any of them is loosened past what you applied.
Tightening is not drift. If you set a 10 minute idle timeout on a realm running the 15 minute baseline, the policy still reads as applied, because a tighter setting still meets it.