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. |
Rotating AUTH_SECRET invalidates every active session — all users are signed out. Treat it as a credential and rotate deliberately. See Security Hardening.
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-sessionreturns a null body (not an error) when unauthenticated or after sign-out, so a client can safely poll it to render auth state.list-sessionsis session-isolated — a user only ever sees their own sessions, each withid,userId, creation, and expiration timestamps.sign-outis idempotent: calling it without a session, or repeatedly, still returns200.
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).
- Cookie attributes —
httpOnly,sameSite=lax, andsecurein production. Not readable by client JavaScript. - Every write is server-validated —
POSTverifies therecordIdis in the user'suser_accessrows for that slug. Out-of-scope writes return403with no state change. - Every read is server-validated — when resolving
$currentUser.activeAssignment, the engine re-checks the cookie againstuser_access. Tampered, stale, or now-revoked values are discarded and the resolver falls back to the user's first accessible record.
Tamper-proof by construction. A user cannot edit the cookie to reach a record outside their assignments — the value is re-validated against user_access on every request, both when set and when read. This makes context-switching safe without re-authenticating.
$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.
Related Pages
- Authentication Overview — enabling auth and the
authblock. - Post-Login Landing —
$currentUser.assignmentsinterpolation and per-role landing. - Table Permissions — row-level
whenpredicates that consume$currentUser. - Security Hardening —
AUTH_SECRETrotation and cookie hardening.