Cookies
@riser-bob/cookie: secure cookie management with signing, rotation and safe defaults.
@riser-bob/cookie reads request cookies, sets and deletes cookies, signs values with HMAC-SHA256 and applies Set-Cookie headers to a response. It is a separate package, and it works with or without the framework. AuthMiddleware uses it to read the token in 'cookie' and 'both' modes.
bun add @riser-bob/cookieUse it in a route
import { CookieManager } from '@riser-bob/cookie' bob.spongGet('/cookie/set', (spongReq, spongRes) => { const cookies = CookieManager.fromRequest(spongReq.request) cookies.set('theme', 'dark', { maxAge: 3600 }) return cookies.applyTo(spongRes.SpongOK({ message: 'Cookie created' }))})Call applyTo() on the final response. Without it, the cookies you set are never sent.
Secure by default
Every cookie starts from these defaults, and any option you pass overrides them.
{ path: '/', httpOnly: true, secure: true, sameSite: 'lax' }const cookies = CookieManager.fromRequest(spongReq.request, { defaults: { secure: process.env.NODE_ENV === 'production' },})CookieManager
new CookieManager(cookieHeader?, options?)| Member | Description |
|---|---|
CookieManager.fromRequest(request, options?) | Creates a manager from a Fetch Request. |
get(name) | Returns the value, or undefined. |
has(name) | Checks presence only. It does not validate a session. |
names() | Names of the cookies in the current view. |
getAll() | A snapshot object of all names and values. |
set(name, value, options?) | Queues a Set-Cookie header. Returns the manager. |
setSigned(name, value, options?) | Same, but signs the value. Needs a secret. |
getSigned(name) | Returns the original value, or undefined if missing, unsigned or tampered. |
delete(name, options?) | Queues an expired cookie so the browser removes it. |
getSetCookieHeaders() | A copy of the pending Set-Cookie headers. |
applyTo(response) | Appends one Set-Cookie header per pending cookie. |
headerRejected | true when the incoming Cookie header was ignored for being too large. |
Manager options
| Option | Description |
|---|---|
secrets | Secrets used by setSigned and getSigned. The first one signs, every one verifies. |
defaults | Cookie options applied to every cookie, replacing the built-in defaults you override. |
maxHeaderBytes | Largest incoming Cookie header that will be parsed. Default 16 KiB. |
Signed cookies
const cookies = CookieManager.fromRequest(spongReq.request, { secrets: [process.env.COOKIE_SECRET!],}) cookies.setSigned('session', sessionId, { maxAge: 60 * 60 * 24 * 7 }) const value = cookies.getSigned('session')- Each secret must be a string of at least 32 characters.
- The first secret signs new cookies. Every secret is tried when verifying, which makes rotation painless.
- The cookie name is part of what is signed, so a valid cookie cannot be copied under another name.
Deleting a cookie
cookies.delete('session', { path: '/', sameSite: 'lax' })The browser only removes a cookie when path, domain and the other attributes match the original, so pass the same ones you used to set it. As with set, the deletion only reaches the browser through applyTo.
Options
| Option | Description |
|---|---|
domain | Domain that can receive the cookie. |
path | URL path for which the cookie is available. |
expires | Absolute expiration date. |
maxAge | Lifetime in seconds. |
httpOnly | Hides the cookie from document.cookie. |
secure | Restricts sending to secure contexts. |
sameSite | 'lax', 'strict' or 'none'. 'none' requires secure. |
priority | 'low', 'medium' or 'high'. |
partitioned | Partitioned storage. Requires secure. |
Limits
maxAgeand a futureexpiresare capped at 400 days.- A serialized cookie larger than 4096 bytes is rejected.
- An incoming
Cookieheader larger than 16 KiB is ignored. Change it withmaxHeaderBytes. __Secure-and__Host-prefixes are validated for you.
CookieError
Mistakes such as a bad name or a missing secret throw a CookieError with a code. Reading cookies never throws.
| Code | Meaning |
|---|---|
INVALID_NAME | Invalid cookie name. |
INVALID_VALUE | The value must be a string. |
INVALID_OPTION | Invalid or incompatible cookie option. |
COOKIE_TOO_LARGE | The serialized cookie exceeds the size limit. |
SERIALIZATION_FAILED | Serialization failed. |
SECRETS_REQUIRED | A secret is needed to sign a cookie. |
WEAK_SECRET | A secret is too short or invalid. |