Session Lifetime#
This page applies to interactive user sessions that obtain tokens through the Keycloak authentication flow. For longer-lived programmatic credentials used by applications and unattended scripts, see API Keys for Programmatic Access instead.
Default Lifetimes#
The shipped realm configuration uses these defaults:
Access token lifespan: 300 seconds (5 minutes)
SSO session idle timeout (refresh-token idle): 1800 seconds (30 minutes)
SSO session max lifespan (absolute cap): 36000 seconds (10 hours)
The refresh_expires_in value returned by Keycloak on each token response is the smaller of the remaining idle window and the remaining max-lifespan window, so it shrinks as the session ages.
These are the shipped defaults from the realm configuration and may differ per deployment.
Refreshing an Access Token#
To refresh an access token before it expires, POST to the Keycloak token endpoint with Content-Type: application/x-www-form-urlencoded and the form fields client_id, grant_type=refresh_token, and refresh_token. Confidential clients must also send client_secret; public clients (including typical PKCE clients) omit it.
The realm name is deployment-specific (often airm) — confirm yours with your platform administrator and substitute it for ${REALM} below.
curl Example#
curl -X POST "${KEYCLOAK_BASE_URL}/realms/${REALM}/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "grant_type=refresh_token" \
-d "refresh_token=${REFRESH_TOKEN}"
Omit the client_secret line for public clients.
Python Example#
import requests
import os
data = {
"client_id": os.environ["CLIENT_ID"],
"grant_type": "refresh_token",
"refresh_token": os.environ["REFRESH_TOKEN"],
}
# Confidential clients only — public/PKCE clients have no secret.
if "CLIENT_SECRET" in os.environ:
data["client_secret"] = os.environ["CLIENT_SECRET"]
response = requests.post(
f"{os.environ['KEYCLOAK_BASE_URL']}/realms/{os.environ['KEYCLOAK_REALM']}/protocol/openid-connect/token",
data=data,
)
response.raise_for_status()
tokens = response.json()
Successful Response#
A successful refresh returns HTTP 200 with a JSON body:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJIUzUxMiIs...",
"refresh_expires_in": 1800,
"token_type": "Bearer",
"scope": "openid profile email"
}
Recommended Client Pattern#
Refresh proactively when within ~30 seconds of
expires_inrather than reactively on 401.Each refresh returns a new
refresh_token; use it for the next refresh. The previous refresh token is not immediately revoked, but treating the new one as canonical avoids drift.After roughly 10 hours of continuous use the SSO max lifespan is reached and the user must re-authenticate; refresh cannot extend a session past this cap.
Expiry Behavior#
When Your Access Token Has Expired#
The AIWB API responds with HTTP 401 and body:
{"detail": "Token expired"}
Companion 401 responses you may encounter for related conditions:
Missing
Authorizationheader:{"detail": "Unauthorized"}(returned withWWW-Authenticate: Bearer)Malformed or otherwise invalid token:
{"detail": "Validation of token failed: <reason>"}
When Your Refresh Token Has Expired#
The Keycloak token endpoint responds with HTTP 400:
{"error": "invalid_grant", "error_description": "Token is not active"}
The client must restart the authentication flow to obtain a fresh token pair.
Operator-Tunable Defaults, Not an SLO#
The lifetimes above are realm settings on the Keycloak server, configurable by your platform administrator. The shape of the flow (response codes, refresh semantics) is stable; the exact numbers may differ per deployment. If precise lifetimes matter for your automation, read expires_in and refresh_expires_in from each token response or confirm the deployed values with your administrator.
When to Use API Keys Instead#
For programmatic or unattended traffic, prefer API Keys for Programmatic Access over the user-session refresh flow.