Docs/Account/Guides/Session Management

Session Management

Sessions represent an authenticated user's connection to your application. This guide covers the full session lifecycle, configurable timeouts, concurrent session handling, and revocation patterns.

Session Lifecycle

A session begins when a user successfully authenticates and ends when they log out, their tokens expire, or the session is revoked. Here's how a session flows from creation to termination:

  1. 1User submits credentials (email + password, optionally MFA). On success, a server-side session record is created.
  2. 2An access token (15min default) and refresh token (7d default) are issued. Both are tied to the session ID.
  3. 3The client uses the access token for API requests. When it expires, the refresh token is exchanged for a new token pair.
  4. 4Each refresh rotates the refresh token (old one is invalidated) and resets the idle timeout timer.
  5. 5The session ends when: the user logs out, the refresh token expires without renewal, the idle timeout fires, or an admin revokes it.
┌──────────┐       ┌──────────────────┐       ┌──────────────┐
│  LOGIN   │──────▶│  SESSION CREATED │──────▶│ TOKENS ISSUED│
└──────────┘       └──────────────────┘       └──────┬───────┘
                                                     │
                              ┌───────────────────────┘
                              ▼
                   ┌─────────────────────┐
                   │ CLIENT USES ACCESS  │◀──────────────────┐
                   │ TOKEN FOR REQUESTS  │                   │
                   └──────────┬──────────┘                   │
                              │ (token expires)              │
                              ▼                              │
                   ┌─────────────────────┐    ┌─────────────┴──────┐
                   │  REFRESH TOKEN      │───▶│ NEW TOKEN PAIR     │
                   │  EXCHANGE           │    │ (old refresh dead) │
                   └──────────┬──────────┘    └────────────────────┘
                              │ (refresh expired / idle timeout / logout)
                              ▼
                   ┌─────────────────────┐
                   │  SESSION ENDED      │
                   └─────────────────────┘

Token Types & Lifetimes

Every session involves three time-bound components. Understanding how they interact is key to configuring session behavior for your use case.

Access Token

Short-lived JWT (default 900s / 15min). Verified locally by your backend using the EdDSA public key. Contains user claims: user ID, tenant ID, roles, permissions, session ID.

access_token_ttl: 900

Refresh Token

Longer-lived opaque token (default 604800s / 7 days). Single-use with rotation — each use returns a new refresh token and invalidates the old one. Exchanged for a new token pair.

refresh_token_ttl: 604800

Session Record

Server-side record (default 30d absolute timeout). Ties together all token pairs issued during the session. Stored in PostgreSQL + Redis for fast lookup.

absolute_timeout: 2592000

Relationship: The access token is always shorter than the refresh token, which is always shorter than (or equal to) the session absolute timeout. If any one of these expires, the user must re-authenticate.

Configuring Session Settings

All session-related settings are configurable per tenant via the settings API. Update the session section to adjust token lifetimes, idle timeout, concurrent session limits, and absolute timeout.

PATCH/api/v1/settings🔒 Auth

Update session configuration for your tenant.

curl -X PATCH https://id.vyntech.com.au/api/v1/settings \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{ "session": { "access_token_ttl": 600, "refresh_token_ttl": 259200, "idle_timeout_seconds": 1800, "max_concurrent_sessions": 3, "absolute_timeout_seconds": 2592000 } }'
Request Body
{
  "session": {
    "access_token_ttl": 600,
    "refresh_token_ttl": 259200,
    "idle_timeout_seconds": 1800,
    "max_concurrent_sessions": 3,
    "absolute_timeout_seconds": 2592000
  }
}

Idle Timeout

When idle_timeout_seconds is greater than 0, sessions are automatically revoked if no token refresh occurs within the configured window. The timer resets on every successful refresh.

  • Active user: Refreshing tokens before expiry keeps the session alive indefinitely (up to absolute timeout).
  • Inactive user: If the user closes the browser and returns after idle_timeout has elapsed, their refresh token will be rejected with a session_expired error.
  • Value of 0: Disables idle timeout entirely. Sessions only end via refresh token expiry, absolute timeout, or explicit revocation.
// Example: 30-minute idle timeout for compliance-sensitive apps
{
  "session": {
    "idle_timeout_seconds": 1800
  }
}

Important: Idle timeout is evaluated server-side at refresh time. If your client refreshes tokens proactively (e.g., 1 minute before access token expiry), the effective idle window for the user is idle_timeout_seconds minus your refresh margin. Plan accordingly.

Concurrent Session Limits

The max_concurrent_sessions setting controls how many active sessions a single user can maintain simultaneously. When a new login would exceed the limit, the oldest session is automatically revoked.

Common Scenarios

Limit = 1

Single-device only. Logging in from a new device immediately terminates the previous session. Use for high-security environments or subscription services preventing credential sharing.

Limit = 5

Reasonable multi-device setup. Allows laptop, phone, tablet, work desktop, and one additional device. The 6th login kicks the oldest session.

Limit = 0

Unlimited sessions. No automatic revocation on new login. Sessions only end via timeout or explicit revocation. Default for most applications.

PATCH/api/v1/settings🔒 Auth

Set concurrent session limit (e.g., single-device mode).

curl -X PATCH https://id.vyntech.com.au/api/v1/settings \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{"session": {"max_concurrent_sessions": 1}}'
Request Body
{
  "session": {
    "max_concurrent_sessions": 1
  }
}

Enforcement behavior: When you lower the limit, existing excess sessions are NOT immediately revoked. The limit is enforced on the next login attempt. To immediately enforce, combine with the revoke-all endpoint.

Absolute Timeout

Regardless of activity, sessions expire after absolute_timeout_seconds. This forces re-authentication even for continuously active users — useful for high-security environments where periodic credential verification is required.

  • Default: 2592000 seconds (30 days). After 30 days, the session ends regardless of refresh activity.
  • High-security: Set to 86400 (24 hours) or 28800 (8 hours) for environments requiring daily or per-shift re-authentication.
  • Value of 0: Disables absolute timeout entirely. Sessions persist as long as the refresh token is renewed within its TTL and idle timeout hasn't fired.
// Example: Force re-authentication every 8 hours (healthcare/finance)
{
  "session": {
    "absolute_timeout_seconds": 28800,
    "idle_timeout_seconds": 1800,
    "refresh_token_ttl": 28800
  }
}

Note:When absolute_timeout is set, ensure refresh_token_ttl does not exceed it. If refresh_token_ttl is longer than absolute_timeout, the session will still end at absolute_timeout regardless of the refresh token's validity.

Revoking Sessions

Sessions can be revoked in three ways: a user logs out, a user revokes all their other sessions, or an admin revokes a specific session. All patterns invalidate associated tokens immediately.

User Logout (Revoke Current Session)

POST/api/v1/auth/logout🔒 Auth

End the current session. Invalidates the access token and refresh token.

curl -X POST https://id.vyntech.com.au/api/v1/auth/logout \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..."

Revoke All Other Sessions

Useful when a user suspects their credentials have been compromised. This terminates all sessions except the current one.

POST/api/v1/sessions/revoke-all🔒 Auth

Revoke all sessions except the current one.

curl -X POST https://id.vyntech.com.au/api/v1/sessions/revoke-all \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{"exclude_current": true}'
Request Body
{
  "exclude_current": true
}

Admin: Revoke a Specific Session

Admins with sessions:deletepermission can revoke any user's session by ID. Use when investigating suspicious activity or responding to a security incident.

DELETE/api/v1/sessions/:id🔒 Auth

Revoke a specific session by ID (admin action).

curl -X DELETE https://id.vyntech.com.au/api/v1/sessions/ses_01H9ABCD5E6F7G8H9I0J \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..."

Refresh Token Rotation & Reuse Detection

Vyntech Account implements automatic refresh token rotation with reuse detection. Every time a refresh token is exchanged, a new one is issued and the old one is immediately invalidated. This is the default behavior — no configuration needed.

How It Works

  • Each refresh token is single-use. After exchange, it becomes invalid.
  • The new refresh token inherits the remaining TTL of the session (not a fresh 7-day window).
  • If a previously-used refresh token is submitted again (reuse), the entire session family is invalidated — all tokens associated with that session are revoked.
  • This protects against token theft: if an attacker steals a refresh token and uses it, the legitimate user's next refresh attempt triggers family invalidation, alerting both parties.
Normal Flow:
  Client → refresh_token_1 → Server
  Server → access_token_2 + refresh_token_2 (refresh_token_1 invalidated)
  Client → refresh_token_2 → Server
  Server → access_token_3 + refresh_token_3 (refresh_token_2 invalidated)

Theft Detection:
  Attacker steals refresh_token_1
  Legitimate client → refresh_token_1 → Server → new tokens (refresh_token_1 invalidated)
  Attacker → refresh_token_1 → Server → REUSE DETECTED!
  Server → entire session invalidated (all tokens revoked)
  Both parties must re-authenticate

Automatic protection: Refresh token rotation and reuse detection are always active. There is no setting to disable them. This provides defense-in-depth against token theft without any implementation effort on your side.

Best Practices

Set idle timeout for compliance-sensitive apps

If your app handles sensitive data (healthcare, finance, government), set idle_timeout_seconds to 1800 (30 minutes) or less. This satisfies common compliance frameworks (HIPAA, PCI-DSS, SOC 2) that require automatic session termination after inactivity.

Keep access tokens short (300–900s)

Access tokens are verified locally without a network call. If one is compromised, it remains valid until expiry. Shorter lifetimes (5–15 minutes) limit the damage window. For high-security scenarios, use 300s (5 minutes).

Use concurrent session limits to prevent credential sharing

If your business model requires per-user licensing, set max_concurrent_sessions to a reasonable number (1–3). Users sharing credentials will constantly kick each other out, naturally discouraging the behavior.

Implement proper token storage

For web apps, store tokens in httpOnly, Secure, SameSite=Strict cookies — never localStorage. For mobile apps, use platform secure storage (iOS Keychain, Android Keystore). This prevents XSS-based token theft.

Refresh proactively, not reactively

Don't wait for a 401 response to trigger a refresh. Instead, refresh 1–2 minutes before access token expiry. This avoids failed requests and provides a smoother user experience. Use the expires_in field from the token response to schedule refreshes.

What's Next

We use cookies and similar technologies to measure traffic and improve the site. You can choose which categories to allow. Manage Preferences.