cookie

js/security/cookie.ts

fino:security/cookie - cookie serialization, parsing, signing, and sealing.

HTTP cookie specification: https://www.rfc-editor.org/rfc/rfc6265

Use this module for HTTP cookie values that need browser-compatible attributes or tamper-evident storage. Plain serialization and parsing handle request and response header syntax. Signing appends an HMAC so the original value can be recovered only when it has not been modified. Sealing encrypts and authenticates the value for cookies that must not be readable by clients.

The helpers do not implement session storage or key rotation. Applications should store secrets outside source code, rotate them deliberately, and avoid putting large or highly sensitive payloads in cookies.

import {
  serializeCookie,
  signCookie,
  verifyCookie,
} from 'fino:security/cookie';

const signed = signCookie('user-123', sessionSecret);
const header = serializeCookie('sid', signed, {
  path: '/',
  httpOnly: true,
  secure: true,
  sameSite: 'Lax',
});
const value = verifyCookie(signed, sessionSecret);

Types

type BufferLike = SecurityBufferLike

Byte-oriented secret input accepted by cookie signing and sealing helpers.

Strings are encoded as UTF-8. Uint8Array, ArrayBuffer, and other ArrayBufferView values are read as bytes.

Interfaces

interface CookieOptions {

Options used when serializing a single Set-Cookie header value.

Attributes are emitted only when provided or set to true. Values are not validated for header injection, browser prefix rules, finite expiration values, and SameSite=None / Secure coupling. Callers should still pass trusted domain, path, and policy strings.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = {
  path: '/',
  httpOnly: true,
  secure: true,
  sameSite: 'Lax',
};

Properties

domain?: string

Optional Domain attribute.

Omit it to create a host-only cookie. The value is emitted unchanged.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { domain: 'example.com' };
path?: string

Optional Path attribute.

Defaults to no emitted path attribute; many applications pass /.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { path: '/' };
expires?: Date

Optional absolute expiration time.

When provided, it is formatted with Date.prototype.toUTCString().

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { expires: new Date(Date.now() + 3600_000) };
maxAge?: number

Optional Max-Age value in seconds.

The value is floored before serialization. Pass 0 to expire the cookie.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { maxAge: 3600 };
httpOnly?: boolean

Whether to emit the HttpOnly attribute.

Defaults to omitted. Enable it for cookies that client-side scripts should not read.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { httpOnly: true };
secure?: boolean

Whether to emit the Secure attribute.

Defaults to omitted. Enable it for cookies that should only be sent over HTTPS, and when using SameSite=None.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { secure: true };
sameSite?: 'Strict' | 'Lax' | 'None'

Optional SameSite policy.

Defaults to omitted. Use None only with secure: true for browser compatibility.

import type { CookieOptions } from 'fino:security/cookie';

const options: CookieOptions = { sameSite: 'Lax' };

Functions

function serializeCookie(name: string, value: string, options: CookieOptions = {}): string

Serialize one Set-Cookie header value, URI-encoding the cookie value.

The cookie name must use valid token characters or the function throws Error. The value is encoded with encodeURIComponent(). Expiration attributes must be finite, SameSite=None requires secure: true, and the __Secure- / __Host- prefixes enforce browser-compatible constraints.

import { serializeCookie } from 'fino:security/cookie';

const header = serializeCookie('sid', 'abc123', {
  path: '/',
  httpOnly: true,
  secure: true,
});

function parseCookieHeader(header: string): Record<string, string>

Parse a Cookie request header into a plain object.

Malformed segments without = are skipped. Names are trimmed; values are URI-decoded with decodeURIComponent(), so malformed percent escapes throw. Later duplicate cookie names overwrite earlier values.

import { parseCookieHeader } from 'fino:security/cookie';

const cookies = parseCookieHeader('sid=abc; theme=dark');

function signCookie(value: string, secret: BufferLike): string

Sign a cookie value with HMAC-SHA-256 and return payload.signature.

The payload is base64url-encoded UTF-8 text, and the signature covers the encoded payload. This authenticates but does not encrypt the cookie value.

import { signCookie } from 'fino:security/cookie';

const signed = signCookie('user-123', 'secret');

function verifyCookie(signed: string, secret: BufferLike): string | null

Verify a signed cookie value and return the original UTF-8 value.

Returns null when the token is malformed, the signature does not match, or the payload cannot be decoded. Signature comparison is timing-safe. This does not check expiration; store and verify any expiry in the signed payload.

import { signCookie, verifyCookie } from 'fino:security/cookie';

const signed = signCookie('user-123', 'secret');
const value = verifyCookie(signed, 'secret');

function sealCookie(value: string, secret: BufferLike): string

Encrypt and authenticate a cookie value using AES-256-GCM and a random IV.

The secret is normalized to a 32-byte key. The returned v1.iv.ciphertext.tag value hides the plaintext and detects tampering. A fresh 96-bit IV is used for each seal operation.

import { sealCookie } from 'fino:security/cookie';

const sealed = sealCookie(JSON.stringify({ sub: 'user-123' }), 'secret');

function unsealCookie(sealed: string, secret: BufferLike): string | null

Decrypt a sealed cookie value and return the original UTF-8 string.

Returns null for unsupported versions, malformed parts, decode failures, or AES-GCM authentication failures. This authenticates and decrypts the value but does not enforce expiration unless you include one in the plaintext.

import { sealCookie, unsealCookie } from 'fino:security/cookie';

const sealed = sealCookie('user-123', 'secret');
const value = unsealCookie(sealed, 'secret');

Classes

class CookieJar {

Mutable cookie jar populated from a request Cookie header.

Queue cookie changes with set() or delete(). Framework integrations can call _apply() to append queued Set-Cookie headers to a response. The jar also updates its in-memory values immediately, so handlers can read their own writes during a request.

import { CookieJar } from 'fino:security/cookie';

const jar = new CookieJar('sid=123');
jar.set('theme', 'dark', { path: '/' });
console.log(jar.get('theme'));

Constructors

constructor(header: string | null)

Parse a Cookie header into a mutable jar.

null creates an empty jar. Parsing uses parseCookieHeader(), including its URI-decoding behavior for cookie values.

const jar = new CookieJar(request.headers.get('cookie'));

Methods

get(name: string): string | undefined

Read one cookie value from the current jar state.

Returns undefined when the cookie was not present or has been deleted locally.

const sid = jar.get('sid');
all(): Record<string, string>

Return all current cookie values as a plain object copy.

const values = jar.all();
set(name: string, value: string, options: CookieOptions = {}): void

Queue a Set-Cookie header and update this jar's current value.

Cookie options are passed to serializeCookie().

jar.set('sid', session.id, { httpOnly: true, path: '/' });
delete(name: string, options: CookieOptions = {}): void

Queue an expired Set-Cookie header and remove this jar's current value.

jar.delete('sid', { path: '/' });