Skip to Content
ConceptsSessions & Token Rotation

Sessions & Token Rotation

When a user logs in to an application powered by Auris, the system creates a session. This session is the authoritative record of the user’s authenticated state. Understanding how sessions work, how tokens rotate, and how Auris detects token theft is essential for building secure applications and troubleshooting authentication issues.

What Is a Session in Auris?

A session in Auris is a server-side record stored in the database (via Prisma), not a client-side cookie or token. Each session contains:

FieldDescription
idUnique session identifier
userIdThe authenticated user
sessionTokenAn opaque token stored in an httpOnly cookie on the client
accessTokenThe most recently issued access token (JWT)
refreshTokenThe most recently issued refresh token (opaque string)
tokenFamilyIdentifier linking all refresh tokens in this session’s rotation chain
createdAtWhen the session was created (used for absolute expiry)
lastActiveAtWhen the session was last used (used for sliding expiry)
expiresAtWhen the session expires
ipAddressIP address from the original login
userAgentBrowser/device identifier from the original login
acrAuthentication Context Class Reference (the authentication strength level)
amrAuthentication Methods Reference (which factors were used: password, otp, webauthn)

The key distinction from many other auth providers: Auris sessions are server-authoritative. The server always knows which sessions exist, which are active, and can revoke any session instantly. There is no reliance on token expiry alone for session termination.

Session Lifecycle

A session moves through a well-defined lifecycle from creation to termination:

Session Creation

When authentication succeeds (after all factors including MFA, adaptive risk checks, and Actions Engine hooks), Auris:

  1. Creates an OAuthSession record in the database
  2. Generates a random sessionToken and sets it as an httpOnly cookie (30-minute TTL, refreshed on each use)
  3. Issues an access token (signed JWT) with the user’s claims, roles, and any custom claims
  4. Issues a refresh token (opaque rt_ prefixed string)
  5. Assigns a tokenFamily identifier that links all future refresh tokens in this session

Active Use

During the session’s active lifetime, the client sends the access token on every API request. The resource server validates the JWT signature locally (via JWKS) without contacting Auris. No database lookup occurs for access token validation, which keeps latency low.

Refresh

When the access token expires, the client sends the refresh token to the token endpoint. Auris:

  1. Looks up the refresh token in the database
  2. Validates that it has not been used before (rotation check)
  3. Validates that the session has not expired (absolute and sliding checks)
  4. Invalidates the old refresh token (marks it as used)
  5. Issues a new access token and a new refresh token
  6. Updates lastActiveAt on the session (resets the sliding window)

Termination

Sessions terminate through several mechanisms, each designed for a different scenario:

MechanismWhen It HappensEffect
Explicit logoutUser clicks “Sign Out”Session deleted, all tokens for this session invalidated
Sliding expiryNo refresh within the inactivity windowSession expires, next refresh attempt fails with SESSION_EXPIRED
Absolute expirySession has been alive longer than the absolute maximumSession expires regardless of activity
Admin revocationAdmin revokes session from ConsoleSession deleted immediately
Reuse detectionAn already-used refresh token is presentedEntire token family revoked (see below)
Account actionUser disabled, deleted, or password changedAll sessions for the user are revoked

Refresh Token Rotation

Refresh token rotation is a security mechanism where every successful token refresh invalidates the old refresh token and issues a new one. Auris implements rotation by default with no opt-out.

Why Rotate?

Without rotation, a stolen refresh token remains valid until its natural expiry (7 days by default). The attacker can use it to obtain new access tokens indefinitely within that window, even if the legitimate user continues using the application.

With rotation, a stolen refresh token can only be used once. If the legitimate user refreshes first (which is the common case), the stolen token becomes invalid. If the attacker refreshes first, the legitimate user’s next refresh attempt triggers reuse detection.

How It Works

Each refresh token in Auris is stored in the database with the following metadata:

{ token: "rt_7fKp2mXa9qN3vB8yR4tL1wC6jD5sE0uH", tokenFamily: "fam_abc123", used: false, createdAt: "2025-02-10T08:15:00Z", sessionId: "sess_xyz789" }

When a refresh request arrives:

  1. Token lookup: Find the refresh token in the database
  2. Used check: If used: true, this is a reuse attempt (see Reuse Detection)
  3. Mark as used: Set used: true on the current token
  4. Issue new tokens: Create a new refresh token with the same tokenFamily and used: false
  5. Return: Send the new access token + refresh token to the client
// Request POST /api/auth/token { "grant_type": "refresh_token", "refresh_token": "rt_7fKp2mXa9qN3vB8yR4tL1wC6jD5sE0uH" } // Response { "ok": true, "data": { "accessToken": "eyJhbGciOiJSUzI1NiJ9...", "refreshToken": "rt_NEW_rotated_token_here...", "expiresIn": 900 } }

The client must always store and use the latest refresh token returned from the token endpoint. If the client fails to update its stored refresh token (for example, due to a network error after the response was sent), the old token is already marked as used. The next refresh attempt will trigger reuse detection and revoke the entire session. SDKs handle this automatically, but custom integrations must be careful to persist the new token before discarding the old one.

Refresh Token Reuse Detection

Reuse detection is the mechanism that catches token theft. If an already-used refresh token is presented to the token endpoint, Auris assumes the token was stolen and takes defensive action.

The Attack Scenario

  1. User Alice logs in. She receives refresh token RT-1.
  2. Attacker steals RT-1 (via XSS, network interception, or device compromise).
  3. Case A: Alice refreshes first. RT-1 is marked as used. She receives RT-2. Later, the attacker tries to use RT-1. Auris sees RT-1 is already used. Reuse detected.
  4. Case B: Attacker refreshes first. RT-1 is marked as used. Attacker receives RT-2a. Later, Alice tries to use RT-1. Auris sees RT-1 is already used. Reuse detected.

In both cases, Auris knows something is wrong because a used token was presented.

What Happens on Reuse Detection

When reuse is detected, Auris revokes the entire token family:

  1. Every refresh token with the same tokenFamily is invalidated
  2. The session associated with the family is terminated
  3. An audit log entry is created with action: 'TOKEN_REUSE_DETECTED'
  4. If the user has notification preferences set, a suspicious login notification is sent

Both the legitimate user and the attacker are logged out. The legitimate user must re-authenticate. This is an intentional security tradeoff: a brief inconvenience for the user is preferable to allowing a stolen token to continue being used.

Token Families and Session Binding

A token family is a chain of refresh tokens linked to a single session. When a session is created, a unique tokenFamily identifier is generated. Every refresh token issued within that session shares the same family ID.

Session created → tokenFamily: "fam_abc123" └─ RT-1 (fam_abc123) → used → RT-2 (fam_abc123) → used → RT-3 (fam_abc123) → active

Token families serve two purposes:

  1. Reuse detection scope: When a reused token is detected, Auris can identify and revoke all tokens in the family, not just the individual token.
  2. Session correlation: All tokens in a family trace back to the same session and the same original authentication event. This makes audit trails coherent.

A user can have multiple active token families — one per device/session. Revoking one family does not affect other families for the same user.

Absolute vs Sliding Expiry

Auris supports two expiry models for sessions, and both can be active simultaneously:

Sliding Expiry (Inactivity Timeout)

The session expires if the user does not refresh their tokens within a configured window. Each successful refresh resets the sliding timer.

  • Default: 7 days of inactivity
  • Use case: Log out inactive users automatically
  • Behavior: lastActiveAt is updated on each refresh. If now - lastActiveAt > slidingWindow, the session is expired.

Absolute Expiry (Maximum Session Lifetime)

The session expires after a fixed duration from creation, regardless of activity.

  • Default: 30 days from session creation
  • Use case: Force re-authentication periodically for security compliance
  • Behavior: now - createdAt > absoluteMax triggers expiry, even if the user has been continuously active

Configuration

Both values are configured in the Auris Console under SettingsSecuritySession Policies:

SettingDescriptionDefault
Sliding ExpiryMaximum inactivity before session expires7 days
Absolute ExpiryMaximum session lifetime from creation30 days
Refresh Token LifetimeHow long a single refresh token is valid (must be ≤ sliding expiry)7 days

When both are configured, the session expires at whichever comes first. For example, with a 7-day sliding window and 30-day absolute max:

  • A user who is active daily will be logged out after 30 days and must re-authenticate
  • A user who stops using the application will be logged out after 7 days of inactivity

For highly sensitive applications (banking, healthcare), consider setting absolute expiry to 8-24 hours. This forces frequent re-authentication regardless of activity, reducing the risk window if a session is compromised.

Multi-Device Sessions

Each device or browser where a user logs in creates a separate, independent session. Sessions are not shared across devices.

How It Works

  • Alice logs in on her laptop. Session S1 is created with token family F1.
  • Alice logs in on her phone. Session S2 is created with token family F2.
  • Alice logs in on a shared computer. Session S3 is created with token family F3.

Each session has its own:

  • Session record in the database
  • Token family
  • Independent expiry timers (sliding and absolute)
  • Independent refresh token rotation chain

Session Management

Users can view and manage their active sessions via the account security page. Administrators can manage sessions for any user in the Auris Console under Users → (select user) → Sessions.

Available actions:

ActionScopeEffect
Revoke single sessionOne deviceTerminates the specific session; user must log in again on that device
Revoke all sessionsAll devicesTerminates every session for the user across all devices
Revoke all other sessionsAll devices except currentUseful when a user suspects account compromise: “Log me out everywhere else”

Session Metadata

Each session stores metadata captured at creation time:

  • IP Address: The IP from which the login occurred
  • User Agent: The browser or client identifier
  • Approximate Location: City/country derived from GeoIP (if suspicious login detection is enabled)
  • Last Active: When the session was last used (refreshed)

This metadata is displayed in the Sessions list and helps users identify unfamiliar sessions.

Relationship Between Keycloak Sessions and Auris Sessions

Auris uses Keycloak as the underlying identity provider. When a user authenticates, two sessions exist:

Keycloak Session

The Keycloak session is created when the user authenticates against Keycloak (password verification, social login callback, SAML assertion). Keycloak manages its own session lifecycle, including SSO session cookies and realm-level session policies.

Auris Session

The Auris session is created after Keycloak authentication succeeds and all Auris-layer checks pass (Actions Engine, adaptive MFA, IP rules, CAPTCHA). The Auris session is the authoritative session for application access.

How They Relate

Key points:

  • Keycloak sessions enable Single Sign-On (SSO) across applications within the same realm. If a user is already authenticated in Keycloak, they can obtain tokens for a second application without re-entering credentials.
  • Auris sessions are per-application. Even if Keycloak SSO is active, each application the user accesses gets its own Auris session with its own token family.
  • Revoking an Auris session does not revoke the Keycloak session (the user may still have SSO access to other applications). To fully sign out, the logout flow calls both Auris session revocation and Keycloak’s end_session_endpoint.
  • Session policies configured in the Auris Console (sliding expiry, absolute expiry) apply to Auris sessions. Keycloak has its own session timeout settings configured at the realm level.

For most deployments, you only need to think about Auris sessions. The Keycloak session layer is transparent to applications using the Auris SDK. The dual-session architecture is relevant primarily when debugging SSO behavior across multiple applications or when configuring Keycloak realm settings directly.

Session Storage and Performance

Auris sessions are stored in PostgreSQL via Prisma. This design choice has important implications:

Durability: Sessions survive server restarts and deployments. Unlike in-memory session stores (Redis-only), database-backed sessions are not lost during infrastructure changes.

Scalability: PostgreSQL handles millions of session records efficiently. Indexes on sessionToken, userId, and tokenFamily ensure fast lookups.

Cleanup: Expired sessions are cleaned up by a periodic cron job (/api/oauth/cron/cleanup). This job deletes expired sessions and authorization codes, keeping the session table size manageable. The cron runs every 5 minutes via Vercel Cron.

Access token validation does not hit the database: Access tokens are JWTs verified locally using the public key from the JWKS endpoint. Only refresh token exchanges and session management operations require database queries.