Skip to main content
View as Markdown

Sessions

When a user authenticates, Sovrium issues a server-managed session (an httpOnly cookie backed by a row in the auth.session table). Sessions are inspectable, listable, and revocable through the auth API, and — for multi-tenant apps — augmented by an active-scope session API that lets a user choose which assignment is currently in context.

Session Configuration

Core session behavior (lifetime, refresh window) is managed at the Better Auth server level, not in the app schema. There is no auth.session config block.

auth:
  strategies:
    - type: emailAndPassword
  # Session lifetime/refresh are Better Auth server defaults — not schema fields.
Concern Where it lives
Session lifetime / refresh Better Auth server defaults (not exposed in app.auth).
Signing & encryption AUTH_SECRET environment variable. See Environment Variables.
Cookie security secure cookies in production; derived from BASE_URL / NODE_ENV.

Inspecting & Ending Sessions

The following endpoints are mounted automatically when auth is configured.

Method Endpoint Purpose
GET /api/auth/get-session Returns the current session and user, or a null body if none.
GET /api/auth/list-sessions Lists all active sessions for the authenticated user.
POST /api/auth/sign-out Invalidates the current session (idempotent — always 200).
POST /api/auth/revoke-session Revokes a specific session by id.
POST /api/auth/revoke-other-sessions Revokes every session except the current one.

Behavior worth noting:

  • get-session returns a null body (not an error) when unauthenticated or after sign-out, so a client can safely poll it to render auth state.
  • list-sessions is session-isolated — a user only ever sees their own sessions, each with id, userId, creation, and expiration timestamps.
  • sign-out is idempotent: calling it without a session, or repeatedly, still returns 200.

Active-Scope Sessions (Multi-Tenant)

Apps that scope data per tenant use the user_access junction: a user can have multiple access rows for the same table (e.g. a fractional CFO assigned to three client companies). The active-scope session API lets that user pick which assignment is currently active, persisted in a per-table cookie and resolved by $currentUser.activeAssignment.

First, declare the scope tables:

auth:
  strategies:
    - type: emailAndPassword
  roles:
    - name: customer-admin
  scopeTables: [clients]
  landingPath: /portal
  # No extra config — active-scope endpoints auto-mount for every scopeTables entry.
Property Description
scopeTables Array of table slugs (from app.tables[].name) that user_access rows may reference. Non-empty; entries must be valid slug identifiers; no duplicates. Validated against app.tables[].name at startup.

Then the engine auto-mounts these endpoints for each entry in scopeTables:

Method Path Behavior
POST /api/session/active-scope/:tableSlug Body { recordId }. Sets the active-scope cookie when recordId is in the user's user_access; returns 403 otherwise (no cookie set).
GET /api/session/active-scope/:tableSlug Returns { tableSlug, recordId } from the cookie, or null when unset.
DELETE /api/session/active-scope/:tableSlug Clears the cookie (returns 204).

A :tableSlug that is not in scopeTables returns 404.

Security Model

The active-scope cookie is named sovrium_active_assignment_<tableSlug> (one per scope table).

  1. Cookie attributeshttpOnly, sameSite=lax, and secure in production. Not readable by client JavaScript.
  2. Every write is server-validatedPOST verifies the recordId is in the user's user_access rows for that slug. Out-of-scope writes return 403 with no state change.
  3. Every read is server-validated — when resolving $currentUser.activeAssignment, the engine re-checks the cookie against user_access. Tampered, stale, or now-revoked values are discarded and the resolver falls back to the user's first accessible record.

$currentUser.activeAssignment is consumable in dataSource.filter predicates and row-level permission when clauses, so every assignment-bound view refreshes automatically when the active scope changes. The related $currentUser.assignments.<table> token (the full set of a user's assignments) is covered in Post-Login Landing.