Skip to content

Signed cookies

Tamper-evident cookies with ctx.cookies.setSigned and getSigned, backed by a cookies.secret config section.

AI generated, pending review Updated 10 days ago · Flare 0.3

You need a session cookie clients can’t forge. Flare signs cookie values with an HMAC using cookies.secret from config. Use ctx.cookies.setSigned to write and ctx.cookies.getSigned to read and verify.

Signing provides integrity, not confidentiality. The value is encoded, not encrypted. Don’t store secrets in a signed cookie.

  1. Configure cookies.secret in flare.json or via FLARE__COOKIES__SECRET (minimum 16 characters when a secret is present).
  2. Optionally declare signedCookies: true on routes that use signed cookies so build() enforces the secret.
  3. Call await ctx.cookies.setSigned(name, value, options?) and await ctx.cookies.getSigned(name).
import { FlareHost, FlareResponse } from "@flare-ts/core";
import { node } from "@flare-ts/core/node";
const host = new FlareHost(node);
host.http.get("/sign", async (ctx) => {
await ctx.cookies.setSigned("session", "user-42");
return new FlareResponse(200, { ok: true });
});
host.http.get("/read", async (ctx) => {
const session = await ctx.cookies.getSigned("session");
return new FlareResponse(200, { session: session ?? null });
});
const app = host.build();
app.run();

Set the secret before build():

Terminal window
FLARE__COOKIES__SECRET=your-long-random-secret

Or in flare.json:

{
"cookies": {
"secret": "your-long-random-secret"
}
}

When a route declares signedCookies: true and no secret is configured, host.build() throws SIGNED_COOKIES_NO_SECRET.

host.http.get("/secure", { signedCookies: true }, () => new FlareResponse(200, { ok: true }));

On controllers, add signedCookies: true to the matching httpContract entry.

  • setSigned emits a base64url-encoded value.signature wire form safe for Set-Cookie.
  • getSigned returns the value when the signature is valid, or undefined when absent or tampered.
  • Without a configured secret, setSigned / getSigned throw at runtime (mapped to 500 unless the route opted into build-time validation).