createSessionStorage
Create encrypted cookie sessions: the data lives in an AES-256-GCM encrypted, signed cookie that Rust guards can verify.
import { createSessionStorage } from '@gio.js/core';
interface UserSession {
userId: string;
flash: string;
}
export const sessions = createSessionStorage<UserSession>();Reference
createSessionStorage<Data>(options?). Data is the shape of the session (default Record<string, unknown>); its values must be JSON-serializable.
| Option | Type | Default | Description |
|---|---|---|---|
cookieName | string | 'gio_session' | The cookie that holds the session - also the one require_session guards read unless they name another. Must be a valid RFC 6265 cookie name. |
secrets | string | string[] | GIO_SESSION_SECRET | The keys. The first encrypts and signs; all of them are accepted when reading. Each must be at least 32 bytes. Leave it unset for a storage a guard checks: guards verify with GIO_SESSION_SECRET only. |
maxAge | number | 604800 | Lifetime in whole seconds (7 days), written into the token and into the cookie's Max-Age. Must be a positive integer. |
cookie | Omit<CookieOptions, 'maxAge' | 'expires'> | {} | Cookie attributes, with the defaults of serializeCookie: HttpOnly, SameSite=Lax, Path=/, Secure in production. |
Returns
A SessionStorage<Data>:
| Field | Type | Default | Description |
|---|---|---|---|
cookieName | string | - | The cookie name in use. |
getSession(source) | Session<Data> | - | Reads the request's session. source is a getServerSideProps context, a route.ts or action request, a GioSocket, a plugin's IPCRequest, a web Request, or the raw Cookie header. A missing, expired or tampered cookie gives an empty session with isNew: true - it never throws. |
commitSession(session, options?) | string | - | Encrypts the session into a Set-Cookie value with a fresh expiry. options.maxAge overrides the lifetime for this cookie. Throws when the cookie would exceed 4096 bytes. |
destroySession() | string | - | A Set-Cookie value that deletes the session cookie. |
Session
| Field | Type | Default | Description |
|---|---|---|---|
isNew | boolean | - | True when the request carried no valid session. |
data | Partial<Data> | - | A shallow copy of the current values. |
get(key) | Data[K] | undefined | - | One value. |
set(key, value) | void | - | Sets a value; undefined removes the key. |
unset(key) | void | - | Removes a key. |
has(key) | boolean | - | Whether the key is set. |
Changes live only in the Session object until you send commitSession(session) as a Set-Cookie header.
Secrets and modes
GIO_SESSION_SECRETholds one secret or several, comma-separated. Each is at least 32 bytes; generate one withnode -e "console.log(require('crypto').randomBytes(32).toString('base64url'))".- Production refuses to create a storage without a secret: the call throws
GIO_SESSION_SECRET is not set.... A module that creates its storage at the top level therefore fails to load, and aroute.tsthat imports it answers500. - Development (
NODE_ENV=development) without a secret uses an ephemeral one - generated by the dev server, which also hands it torequire_sessionguards, or by the worker process itself when it runs without one - and logs a warning: sessions reset when it restarts. - A
secretsoption whose first secret is not in a setGIO_SESSION_SECRETlogs a warning, since a guard on that cookie would reject every session.
Errors
Bad configuration throws at creation, not on the first login: a missing secret in production, a secret under 32 bytes, an invalid cookieName, a maxAge that is not a positive integer, and cookie options browsers would reject (for example sameSite: 'none' with secure: false).
Examples
Log in
import type { GioRequest } from '@gio.js/core';
import { sessions } from '../../../lib/session.server.ts';
export async function POST(req: GioRequest) {
const form = await req.formData();
const user = await verifyPassword(String(form.get('email')), String(form.get('password')));
if (user === null) return new Response('Wrong email or password', { status: 401 });
const session = sessions.getSession(req);
session.set('userId', user.id);
return new Response(null, {
status: 303,
headers: { Location: '/dashboard', 'Set-Cookie': sessions.commitSession(session) },
});
}Read it in a page
import { redirect, type GetServerSideProps } from '@gio.js/core';
import { sessions } from '../../lib/session.server.ts';
export const getServerSideProps = (async (ctx) => {
const userId = sessions.getSession(ctx).get('userId');
if (userId === undefined) throw redirect('/login');
return { props: { user: await db.users.find(userId) } };
}) satisfies GetServerSideProps;Log out
import { sessions } from '../../../lib/session.server.ts';
export function POST() {
return new Response(null, {
status: 303,
headers: { Location: '/', 'Set-Cookie': sessions.destroySession() },
});
}Protect a section in Rust
A guard checks the session cookie's signature and expiry before any Node code runs, with GIO_SESSION_SECRET:
[[guards]]
path = "/dashboard/*rest"
require_session = true
redirect_to = "/login"Good to know
- Reading a session makes the page personal. Reading
ctx.cookies(whichgetSession(ctx)does) keeps the render out of the shared page cache. - Stateless. Logging out deletes the browser's cookie, but a copied cookie stays valid until it expires. Keep
maxAgeshort, or store a per-user session version server-side and compare it, when logout must end every copy. - Not rolling. The expiry is set when you commit. To extend a session on activity, commit it again.
- Size. Keep ids and small flags in the session; the whole cookie must stay under 4096 bytes.
- The cookie name is bound into the token. Changing
cookieNamelogs everyone out, and a token cannot be replayed under another cookie that shares the secret. - Rotation. Prepend a new secret (
GIO_SESSION_SECRET=new,old), restart, and remove the old one aftermaxAge. Removing a secret invalidates every session it signed at once. - Tests.
renderPageandcallRouteset a random secret for the test process when none is configured, so session modules load there.
Related
- Authentication - the full login flow, guards and rotation
- Authentication Example
- Cookie helpers
- [[guards]]
- Environment variables -
GIO_SESSION_SECRET
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Introduced. |