Cookies

Cookie names used by the module, their security attributes, the signed cookie format, and the makeCookie utility for setting cookies in custom handlers.

The module uses a fixed set of cookies to manage session state. Each cookie has specific security attributes that enforce origin binding, transport security, and script isolation.


NameContentHttpOnlySameSiteSecureSet by
__Secure-aAccess tokenYesStrictYesLogin, OAuth callback, token rotation
a-iatAccess token issued-at timestampYesStrictYesLogin, OAuth callback, token rotation
sessionRefresh tokenYesStrictYesIAM service (forwarded)
__Host-csrfSigned CSRF tokenNoStrictYesgenerateCsrfCookie
__Host-dr_i_nSigned visitor cookie (skips bot check)YesStrictYesbotDetectorMiddleware / checkForBots()
canary_idVisitor fingerprint identifierYesStrictYesIAM service (forwarded)
state{name}OAuth state (signed)YesLax or NoneYesOAuthRedirect (3 min TTL)
pkce_v{name}PKCE verifierYesLax or NoneYesOAuthRedirect (3 min TTL)
nonce{name}OIDC nonceYesLax or NoneYesOAuthRedirect (3 min TTL)

The __Secure- prefix requires Secure: true. The __Host- prefix additionally requires Path: / and no Domain attribute, meaning the cookie is scoped to the exact origin with no subdomain access.

OAuth flow cookies (state{name}, pkce_v{name}, nonce{name}) use SameSite: None for providers that use response_mode: form_post, and SameSite: Lax otherwise.


makeCookie

makeCookie(event, name, value, options) sets an HTTP cookie on the response. It is a thin wrapper around the H3 setCookie function with typed options.

server/api/custom.post.ts
makeCookie(event, '__Secure-custom', value, {
  httpOnly: true,
  sameSite: 'strict',
  secure: true,
  path: '/',
  domain: domain,
  maxAge: 900 // 15 minutes
})

Use makeCookie in custom handlers whenever you need to set a cookie with the same attribute pattern as the session cookies. Pass maxAge in seconds.


Signed cookies

The module uses HMAC-signed cookies for the CSRF token and OAuth state. The signed format is:

base64(value).base64(keyword).expiry.hmac

Two functions manage this format:

createSignedValue(raw, ttlMs, keyword) creates a signed cookie string. The keyword binds the signature to a specific context so that a signed value from one context cannot be replayed in another.

verifySignedValue(cookie, keyword) parses and verifies the signature and expiry. It returns { valid: boolean, payload: { value, exp } }.

const signed = createSignedValue('my-token', 1000 * 60 * 5, 'my-context')

makeCookie(event, '__Secure-custom', signed, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 300
})
const { valid, payload } = verifySignedValue(cookieValue, 'my-context')

if (!valid || !payload) {
  throw createError({ statusCode: 403 })
}

const rawToken = payload.value

The cryptoCookiesSecret field in the configuration is the HMAC key used for all signed cookies. It should be at least 32 bytes of random data and must remain stable across application restarts.


The __Secure-a access token cookie and a-iat issued-at cookie are always set with the domain and maxAge values fetched from getOperationalConfig. That function calls the IAM service at GET /operational/config, parses the response, and caches the result for 24 hours keyed by the IAM server address. The cache means the IAM is called at most once per process lifetime for this data.

Two values come from that response:

  • domain: the cookie domain configured on the IAM service. Used as the Domain attribute on __Secure-a and a-iat, scoping them across subdomains of that domain.
  • accessTokenTTL: the IAM returns this value in milliseconds. The gateway converts it to seconds with Math.floor(ms / 1000) and uses it as the MaxAge of both cookies.

Clearing cookies on logout

logoutHandler deletes all session cookies explicitly by calling deleteCookie with matching attributes. The cookie deletion must use the same domain, path, secure, httpOnly, and sameSite values as the original Set-Cookie call. Using makeCookie with an empty value and maxAge: 0 achieves the same result for custom flows.

Logo