Skip to content

Token Lifecycle

import { Aside } from ‘@astrojs/starlight/components’;

Understanding how tokens work in Kotauth prevents a class of subtle integration bugs and security issues. This page covers what each token is, how long it lives, and what happens when it’s rotated or revoked.

A short-lived RS256-signed JWT. Resource servers verify it using the workspace JWKS endpoint — no call to Kotauth per request.

Default lifetime: 300 seconds (5 minutes). Configurable per application in the admin console.

Contents:

{
"iss": "https://auth.yourdomain.com/t/my-app",
"sub": "42",
"aud": ["my-spa"],
"exp": 1735689600,
"iat": 1735689300,
"name": "Alice Smith",
"email": "alice@example.com",
"email_verified": true,
"preferred_username": "alice",
"realm_access": {
"roles": ["admin"]
},
"resource_access": {
"my-spa": {
"roles": ["orders:write"]
}
}
}

Access tokens are not stored in the Kotauth database. Once issued, they are verified purely by JWT signature. Revoking access requires waiting for expiry or accepting a short window of continued access (the token lifetime).

A long-lived opaque token. Only meaningful to Kotauth — resource servers never see it. Stored in the database as a SHA-256 hash.

Default lifetime: 86400 seconds (24 hours). Configurable per workspace.

Used to obtain a new access token without re-authenticating. Returns a new access token and a new refresh token on every use (rotation).

A JWT issued alongside the access token during OIDC flows. Contains user identity claims. Intended for the client application, not resource servers. Use it to display user info in your UI, not to protect API endpoints.

Refresh tokens rotate on every use. When you exchange a refresh token for a new access token, you get a new refresh token in the response. The previous refresh token is immediately invalidated.

Client Kotauth
│ │
│──── refresh_token_1 ─>│ ✓ Valid, issue new tokens
│<──── access_token_2 ──│
│<──── refresh_token_2 ─│ ← Store this, discard refresh_token_1
│ │
│ (if replay attempted:)
│──── refresh_token_1 ─>│ ✗ Rejected — session revoked

These settings live in the admin console per workspace (or per application for access token TTL):

SettingDefaultNotes
Access token TTL300s (5 min)Configurable per application
Refresh token TTL86400s (24h)Workspace-wide
Email verification token24hFixed
Password reset token1hFixed

A session is the server-side record associated with a refresh token. Revoking a session:

  • Invalidates the refresh token immediately
  • Does not immediately invalidate in-flight access tokens (they are stateless JWTs)
  • Prevents issuance of any new tokens for that session

Sessions can be revoked by:

  • The user from the self-service portal (/t/{slug}/account/sessions)
  • An admin from the admin console or REST API (DELETE /sessions/{id})
  • The logout endpoint (/t/{slug}/protocol/openid-connect/logout)
  • Replay detection (automatic, as described above)

Resource servers should validate:

  1. Signature — using the JWKS endpoint (/t/{slug}/protocol/openid-connect/certs)
  2. exp claim — token has not expired
  3. iss claim — matches your expected issuer (https://auth.yourdomain.com/t/{slug})
  4. aud claim — contains your application’s client ID

Most OAuth2 libraries handle all four checks when given the OIDC discovery URL.

If your resource server cannot verify JWTs locally (uncommon, but possible with opaque tokens or when you need real-time revocation checks), use the introspection endpoint:

POST /t/{slug}/protocol/openid-connect/introspect
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
token=ACCESS_TOKEN&token_type_hint=access_token

See Introspection & Revocation for details.