Signed session

createSession (from @bext-stack/framework/auth) sets an HMAC-signed cookie; read() verifies the signature and the expiry on every request. Tampered values fail constant-time comparison and the request is treated as anonymous. The signer is pure-JS HMAC-SHA256 — no node:crypto dependency.

Tip
Expiry is baked into the signed payload, not just the cookie Max-Age — a token copied from DevTools still expires server-side. Always read the secret from an environment variable rather than a constant. The same module also provides parseCookies, PKCE, and OIDC authorization-URL building.

Status

src/app/examples/session/page.tsxTSX
import { createSession } from "@bext-stack/framework/auth";

// Pure-JS HMAC-SHA256 signer — no node:crypto, runs in any isolate.
const session = createSession<{ name: string }>({
  secret: process.env.SESSION_SECRET!,
  cookie: "sid",
  maxAgeSecs: 600,   // expiry is signed into the payload, not just the cookie
});

export async function loader({ request }) {
  // verifies the signature + expiry; tampered / expired → null
  return { session: session.read(request) };
}

export async function action({ request }) {
  const form = await request.formData();
  if (form.get("op") === "logout")
    return new Response(null, { status: 303, headers: { location: "/", "set-cookie": session.clearCookie() } });
  const name = String(form.get("name"));
  return new Response(null, {
    status: 303,
    headers: { location: "/", "set-cookie": session.cookie({ name, loginAt: Date.now() }) },
  });
}