js/net/http/session
js/net/http/session.ts
internal:net/http/session — revision-safe HTTP app session implementation.
The public API is exported only by fino:net/http/app. Callers supply a
an atomic, expiry-capable fino:store backend directly; session lifecycle code owns
record validation, TTL translation, sealed identifiers, and conditional
writes.
Session data uses the supplied provider's value representation and must be structured-cloneable as well as supported by that provider. It is suitable for authentication identity and small request-scoped metadata, not as a transactional application database. A distributed adapter must provide atomic per-key conditional writes and read-after-write behavior to preserve the security guarantees of invalidation and regeneration; eventual replication alone is insufficient for immediate global logout.
import { memoryStore } from 'fino:store';
import { sessions } from 'fino:net/http/app';
const middleware = sessions({
store: memoryStore(),
keys: [{ id: 'primary', secret: process.env.SESSION_SECRET! }],
ttlMs: 3_600_000,
});Interfaces
interface SessionClock {
Clock used for session timestamps and expiry checks.
Methods
now(): number
Return the current Unix timestamp in milliseconds.
interface SessionRecord<T = Record<string, unknown>> {
Durable, backend-neutral representation of one server session.
Properties
id: string
Opaque session identifier stored only inside the sealed browser cookie.
data: T
Structured-cloneable application data accepted by the configured store.
createdAt: number
Unix timestamp in milliseconds when this session was first created.
updatedAt: number
Unix timestamp in milliseconds for the last application-data update.
expiresAt: number
Absolute Unix timestamp in milliseconds after which the record is invalid.
interface SessionKey {
One cookie-sealing key accepted by server-session middleware.
Properties
id: string
Stable short identifier written outside the sealed cookie payload.
secret: BufferLike
Secret material used by AES-256-GCM cookie sealing.
interface Session<T = Record<string, unknown>> extends SessionRecord<T> {
Request-local session exposed to HTTP application handlers.
Properties
isNew: boolean
Whether this request created or regenerated the session.
Methods
regenerate(): void
Replace the session ID while retaining its data.
Call this after authentication succeeds to prevent session fixation. The old ID is deleted before the replacement is committed.
invalidate(): void
Delete the stored session and expire its browser cookie after the response.
interface SessionOptions<T = Record<string, unknown>> {
Options for the secure HTTP session producer.
Properties
store: AtomicExpiringStore
Caller-owned generic store with atomic commit and provider-managed expiry.
keys: readonly [SessionKey, ...SessionKey[]]
Sealing keys ordered primary-first; old keys remain readable for rotation.
ttlMs: number
Session lifetime in milliseconds. Must be finite and greater than zero.
cookie?: string
Cookie name. Defaults to fino.sid.
cookieOptions?: CookieOptions
Additional cookie policy merged over secure defaults.
rolling?: boolean
Extend expiry after each successful request. Defaults to false.
clock?: SessionClock
Clock used for deterministic timestamps and expiry.
Classes
class SessionConflictError extends Error {
Error raised when a request tries to commit data based on a stale revision.
The middleware never guesses how to merge arbitrary session data or reruns a handler whose side effects may already have happened. Applications may turn this error into a conflict response or ask the client to retry safely.
Readonly Properties
readonly sessionId: string
Conflicting session identifier.
Constructors
constructor(sessionId: string)
Create a conflict error for sessionId.
Functions
function sessions<T = Record<string, unknown>>(options: SessionOptions<T>): Producer
Create an HTTP app producer that loads and commits a secure server session.
Install it with .value('session', sessions(options)), normally after the
app's cookies() producer. The cookie contains only an AES-GCM-sealed
session ID. New cookies use the first key; cookies opened with a later key
are automatically resealed with the primary key after a successful request.
Unchanged fixed-expiry sessions perform no store write. Rolling sessions
conditionally extend expiry. Concurrent expiry-only updates may retry against
the latest record, while conflicting application-data mutations raise
SessionConflictError rather than lose an update.
For OAuth/OIDC callbacks, capture the request session in
oauthCallback({ onSuccess }), call session.regenerate() after the token
exchange succeeds, and copy only the verified identity claims the
application needs into session.data. Provider access and refresh tokens
are not stored automatically.
import { memoryStore } from 'fino:store';
import { App, cookies, sessions } from 'fino:net/http/app';
const app = new App();
const authenticated = app.value('cookies', cookies()).value('session', sessions({
store: memoryStore(),
keys: [{ id: '2026-07', secret: process.env.SESSION_SECRET! }],
ttlMs: 24 * 60 * 60_000,
}));
authenticated.get('/me').handle((ctx) => Response.json(ctx.session.data));