Sessions and authentication
Store opaque signed sessions, rotate credentials, protect authentication boundaries, and keep request identity isolated during SSR.
Create session storage
Tavo.js stores only a signed opaque session ID in the cookie. Session data stays in a SessionStore such as a database or Redis adapter. The built-in memory store is bounded but process-local, so reserve it for tests and local development.
cookie.nameand at least one non-empty secret are required. Every secret must contain at least 32 UTF-8 bytes.Cookie defaults are Path=/,
HttpOnlyenabled, andSameSite=Lax. Secure is inferred from an HTTPS request unless explicitly configured.Secrets are checked in array order and new cookies are signed with the first secret. Put the new secret first and retain old secrets during a rotation window.
tsimport "@tavojs/core/server-only";
import { createSessionStorage } from "@tavojs/core/server";
export const sessions = createSessionStorage<{ userId?: string }>({
cookie: {
name: "__session",
secrets: [process.env.SESSION_SECRET!],
maxAge: 60 * 60 * 24 * 7,
sameSite: "lax"
},
store: databaseSessionStore
});Session and storage API
getSessionaccepts a Request or an object with a request property. Missing, invalid, or expired cookies create a new empty session.rotate replaces the opaque ID at the next commit and deletes the old store entry. Use it after login or a privilege change.
destroy marks the session for deletion. A later commit returns an expired cookie and removes the store entry.
redirect commits the session, appends Set-Cookie, normalizes the Location target, and defaults to status 303.
tstype Session<T> = {
readonly data: T;
readonly id: string;
readonly isNew: boolean;
readonly rotated: boolean;
readonly secure: boolean;
get(key): T[key] | undefined;
has(key): boolean;
set(key, value): void;
delete(key): void;
rotate(): void;
destroy(): void;
};
sessions.getSession(request): Promise<Session<T>>
sessions.commitSession(session, { maxAge? }): Promise<string>
sessions.destroySession(session, { maxAge? }): Promise<string>
sessions.redirect(to, session, init?): Promise<Response>Custom and memory stores
The memory limit defaults to 10,000. Oldest entries are evicted when capacity is exceeded; expired entries are removed when read.
Set
maxEntriesto zero to disable persistence. A negative, infinite, or non-numeric limit throws.commit
maxAgeoverrides the cookiemaxAgefor that response. Expiry is stored alongside server data and serialized into the cookie.Production stores must apply expiration consistently and support every runtime instance that can receive the user's next request.
tstype SessionStore<T> = {
get(id: string): MaybePromise<{ data: T; expiresAt: number | null } | null>;
set(id: string, entry: { data: T; expiresAt: number | null }): MaybePromise<void>;
delete(id: string): MaybePromise<void>;
};
const memory = createMemorySessionStore({ maxEntries: 10_000 });
memory.size();Authenticate every request
Read authentication in server middleware, loaders, actions, or plugin handlers for every protected request.
After verifying login, rotate the session ID, set the user ID, and return
sessions.redirectso the cookie is committed.Send only safe profile fields into loader data. Never expose the session ID, cookie, signing secret, or access token.
A client auth store may mirror safe user data after hydration, but the server must still authorize each request from the session backend.
tsexport const middleware = defineServerMiddleware(async ({ request }) => {
const session = await sessions.getSession(request);
if (!session.get("userId")) return { redirect: "/login", status: 302 };
});
export const load = defineServerLoader(async ({ request }) => {
const session = await sessions.getSession(request);
const user = await findUser(session.get("userId"));
return { user: user ? { id: user.id, name: user.name } : null };
});Look up exact public types
Follow linked API names to their canonical TypeScript declarations and package boundaries.