Sessions
Understand session validation, rotation, and revocation.
A session is effect-auth's durable proof that a particular authentication ceremony completed for a user. It is intentionally a technical session, not a user profile: the current-session response identifies the user and records authentication assurance, but it does not load names, preferences, roles, or an application user object. Use userId to authorize the request and, when needed, expose a separate application-owned user/profile endpoint.
Cookie and server row
The browser receives an opaque token shaped as sessionId.secret. SessionCookie reads and serializes that token; by default it uses __Host-session, Secure, HttpOnly, SameSite=Lax, and Path=/. JavaScript should not read or persist it. The browser sends it with requests while the server keeps the authoritative session row.
| Value | Where it lives | Purpose |
|---|---|---|
sessionId | Cookie and row | Selects the session record |
Secret / secretHash | Raw secret only in cookie; hash in row | Proves possession without storing the bearer secret |
userId | Row and responses | Connects the session to the application identity |
| Authentication events | Row only | Server-side provenance used to derive assurance and freshness |
authTime, aal, amr, mfaVerifiedAt | Row and responses | Derived summaries for guards and browser UI |
createdAt, lastSeenAt, expiresAt, revokedAt | Row | Controls activity, expiry, listing, and revocation |
The stored secret is an HMAC-SHA-256 digest made with the configured session key. Validation hashes the presented secret and compares it safely with secretHash; a database read alone therefore does not reveal a usable cookie. Protect both the session key and the cookie, because either is security-sensitive.
Lifecycle
authentication succeeds
|
v
create row + issue token --> commit Set-Cookie --> validate each request
^ |
| v
+----- rotate secret <----- refresh idle expiry when due
| |
+---- old token fails |
v
revoke / expire --> unauthenticated
|
clear cookieAn authentication flow creates the row and returns an IssuedSession. At the standard HTTP boundary, AuthHttp commits it through SessionCookie; application code should not manually return the token. Password, MFA, and other built-in flows own this create-and-commit transition.
Every authenticated operation reads the cookie and validates the row. Validation rejects malformed tokens, missing rows, secret mismatches, revoked sessions, and sessions past either expiry limit. The default policy has a 30-day idle TTL, a 90-day absolute TTL, and a one-day refresh threshold. Effective expiry is the earlier of idle expiry and authTime + absoluteTtl.
Refresh validates the same token and, when due, advances lastSeenAt and idle expiry without crossing the absolute limit. It does not change the secret. Rotation generates a new secret for the existing session ID and row. Assurance transitions use a compare-and-set mutation that appends verified evidence and rotates atomically: exactly one concurrent transition wins, the old token is invalid immediately, and the caller receives one replacement token. The standard factor-specific HTTP operations commit that replacement cookie. A custom transport must commit it itself and handle a response lost after storage commits.
Revocation marks server state, so it takes effect on the next validation even if a browser still holds its cookie. Logout attempts to revoke the current valid row and clears the cookie; an absent or already-invalid cookie is still cleared. The standard revoke endpoint verifies that the target session belongs to the authenticated user. Revoking the current session also clears its cookie, while revokeOthers preserves the current row. Listing returns only active sessions for that user. Storage failures are server errors; expected invalid, expired, or revoked credentials become an unauthenticated response rather than exposing why validation failed.
Authentication context
Authentication events are the source of truth. Core derives aal, canonical amr, authTime, and mfaVerifiedAt; callers do not select them. Method freshness and stable-factor freshness use the matching event's verifiedAt, not one shared session timestamp. Raw events include credential/factor provenance and remain server-only. Normal HTTP schemas and client session types expose only derived summaries.
The local tiers are aal1 for a verified basic authenticator/channel, aal2 for a verified strong authenticator or independent second factor under the central policy, and aal3 for explicitly registered custom hardware-bound policy; no built-in flow establishes aal3. These project-local names are inspired by AAL terminology. Effect Auth does not automatically provide or claim NIST SP 800-63 conformance. Conformance depends on the complete authenticator, identity proofing, lifecycle, deployment, and operational controls.
Canonical built-in AMR values are pwd, email_otp, magic_link, oauth, totp, passkey, and recovery_code. A passkey reaches local aal2 only when user verification was verified by the server. Recovery-code evidence creates constrained remediation state and is not ordinary aal2.
The browser client exposes the standard session operations without exposing the cookie:
import { createAuthClient } from "@effect-auth/core/Client";
const auth = createAuthClient();
const session = await auth.session.currentOrUndefined();
if (session?.aal !== "aal2") {
// Start the application's step-up flow before the sensitive operation.
}currentOrUndefined converts only the typed unauthenticated result to undefined; network, decoding, and internal failures still reject. auth.session.refresh() explicitly refreshes and recommits expiry, while list, revoke, revokeOthers, and logout support account session management. See Browser Client for transport and cross-origin cookie requirements.
Ownership
Prefer the standard HttpApi session operations when their behavior fits: they own cookie reading, public error mapping, origin protection for mutating operations, user-scoped revocation, and response commitment. Calling the public Sessions or SessionCookie services directly is appropriate for a custom protocol, but then the application owns those omitted transport and security decisions, including committing every newly issued or rotated token and clearing cookies after revocation.
Continue with Password, MFA, Step-up, and Security Policies.