@samveys/security
CSRF guard (Fetch Metadata + tokens), CORS, rate limiting
import { cors, corsPreflight, csrf, csrfToken, rateLimit, security, … } from '@samveys/security'
Function (6)
cors
Function
CORS with exact origin matching. A preflight reaching this middleware (for example on a route you mounted for `OPTIONS`) is answered 204 with the allow headers. Other responses to an allowed origin get `Access-Control-Allow-Origin` and `Vary: Origin` (plus `Allow-Credentials` when set). A disallowed origin gets no `Access-Control-*` header at all, so the browser blocks the script.
function cors(options: CorsOptions): Middleware;
Exported from packages/security/src/cors.ts
corsPreflight
Function
Answers a CORS preflight (`OPTIONS` with `Access-Control-Request-Method`) from an allowed origin with 204 and the allow headers; returns `undefined` for anything else, so a disallowed origin gets no `Access-Control-*` header. Router-less: the `security()` plugin runs it in the `http:request` hook, because the router has no `OPTIONS` routes.
function corsPreflight(options: CorsOptions): (ctx: Pick<HttpContext, 'method' | 'headers'>) => Response | undefined;
Exported from packages/security/src/cors.ts
csrf
Function
CSRF guard from Fetch Metadata, `Origin`, and an optional form token. State-changing requests (anything but GET, HEAD, OPTIONS) are checked in this order: 1. `api: true` and a `POST` with a simple content type → rejected. 2. `Sec-Fetch-Site` `same-origin`/`none` → allowed; `same-site`/`cross-site` → rejected unless `Origin` is in `allowOrigins`/`trustedOrigins`. 3. Without it, `Origin` must be this server's origin (`ctx.url.origin`), a trusted origin or an allowed one. 4. With neither header, a valid form token (`x-csrf-token` header or `_csrf` field) is required. Every response gets `Vary: Sec-Fetch-Site, Origin`. A rejection is SVS_E091 (403).
function csrf(options?: CsrfOptions): Middleware;
Exported from packages/security/src/csrf.ts
csrfToken
Function
The form token for this request: an HMAC of the browser's random cookie value. Put it in a hidden `_csrf` field or send it as `x-csrf-token`. Calling it makes the middleware set the cookie. Only works on routes that use the `csrf` middleware.
function csrfToken(ctx: Pick<HttpContext, 'state'>): string;
Exported from packages/security/src/csrf.ts
rateLimit
Function
Fixed-window rate limiter on a cache store's atomic `increment`, so the count is shared by every process using the same store (database store, not the memory store). Over the limit it answers 429 SVS_E092 with `Retry-After` in seconds. If the store fails the request is let through and the failure is logged: a broken cache must not take the API down.
function rateLimit(options: RateLimitOptions): Middleware;
Exported from packages/security/src/rate-limit.ts
security
Function
The `@samveys/security` plugin. Registers the named middleware `csrf` and `cors` (use them with `use: ['csrf']`) and, when `rateLimit` is given, `rateLimit` backed by the `@samveys/cache` plugin, which is then required. Invalid options (e.g. `*` with credentials) throw right here.
function security(options?: SecurityOptions): import("@samveys/core").Plugin<SecurityOptions>;
Exported from packages/security/src/plugin.ts
Value (1)
CSRF_COOKIE
Value
Name of the cookie holding the per-browser random value the form token is derived from.
const CSRF_COOKIE = "__Host-svs-csrf";
Exported from packages/security/src/csrf.ts
Interface (4)
CorsOptions
Interface
Options of {@link cors}.
interface CorsOptions {
/** Exact origins, e.g. `https://app.example.com`. `'*'` allows any origin but not with `credentials`. */
readonly origins: readonly string[];
/** Default `GET, HEAD, PUT, PATCH, POST, DELETE`. */
readonly methods?: readonly string[];
/** Request headers a preflight may ask for. Default: whatever the preflight asked for. */
readonly allowHeaders?: readonly string[];
/** Response headers scripts may read. */
readonly exposeHeaders?: readonly string[];
/** Send `Access-Control-Allow-Credentials: true`. Not allowed together with `origins: ['*']`. */
…
Show the whole declaration (14 lines)
interface CorsOptions {
/** Exact origins, e.g. `https://app.example.com`. `'*'` allows any origin but not with `credentials`. */
readonly origins: readonly string[];
/** Default `GET, HEAD, PUT, PATCH, POST, DELETE`. */
readonly methods?: readonly string[];
/** Request headers a preflight may ask for. Default: whatever the preflight asked for. */
readonly allowHeaders?: readonly string[];
/** Response headers scripts may read. */
readonly exposeHeaders?: readonly string[];
/** Send `Access-Control-Allow-Credentials: true`. Not allowed together with `origins: ['*']`. */
…interface CorsOptions {
/** Exact origins, e.g. `https://app.example.com`. `'*'` allows any origin but not with `credentials`. */
readonly origins: readonly string[];
/** Default `GET, HEAD, PUT, PATCH, POST, DELETE`. */
readonly methods?: readonly string[];
/** Request headers a preflight may ask for. Default: whatever the preflight asked for. */
readonly allowHeaders?: readonly string[];
/** Response headers scripts may read. */
readonly exposeHeaders?: readonly string[];
/** Send `Access-Control-Allow-Credentials: true`. Not allowed together with `origins: ['*']`. */
readonly credentials?: boolean;
/** Preflight cache time in seconds. Default 600. */
readonly maxAge?: number;
}
Exported from packages/security/src/contracts.ts
CsrfOptions
Interface
Options of {@link csrf}.
interface CsrfOptions {
/**
* `'enforce'` (default) rejects with SVS_E091 (403). `'log'` lets the request through and records
* the rejection with `logger.warn`, for a safe roll-out.
*/
readonly mode?: 'enforce' | 'log';
/** JSON API: reject a `POST` with a simple content type (`text/plain`, urlencoded, multipart). */
readonly api?: boolean;
/** Other origins that may send state-changing requests (e.g. a separate front-end). Exact match. */
readonly allowOrigins?: readonly string[];
…
Show the whole declaration (23 lines)
interface CsrfOptions {
/**
* `'enforce'` (default) rejects with SVS_E091 (403). `'log'` lets the request through and records
* the rejection with `logger.warn`, for a safe roll-out.
*/
readonly mode?: 'enforce' | 'log';
/** JSON API: reject a `POST` with a simple content type (`text/plain`, urlencoded, multipart). */
readonly api?: boolean;
/** Other origins that may send state-changing requests (e.g. a separate front-end). Exact match. */
readonly allowOrigins?: readonly string[];
…interface CsrfOptions {
/**
* `'enforce'` (default) rejects with SVS_E091 (403). `'log'` lets the request through and records
* the rejection with `logger.warn`, for a safe roll-out.
*/
readonly mode?: 'enforce' | 'log';
/** JSON API: reject a `POST` with a simple content type (`text/plain`, urlencoded, multipart). */
readonly api?: boolean;
/** Other origins that may send state-changing requests (e.g. a separate front-end). Exact match. */
readonly allowOrigins?: readonly string[];
/**
* Origins that count as this server's own origin. Use behind a TLS-terminating proxy, where
* `ctx.url.origin` is `http://…` but browsers send `https://…`.
*/
readonly trustedOrigins?: readonly string[];
/**
* HMAC key for form tokens. When omitted a random key is made per process: tokens then stop
* working after a restart and across several instances, so set it in production.
*/
readonly secret?: string;
/** Receives log-mode rejections. The `security()` plugin passes its own logger. */
readonly logger?: Logger;
}
Exported from packages/security/src/contracts.ts
RateLimitOptions
Interface
Options of {@link rateLimit}.
interface RateLimitOptions {
/** Requests allowed per window and key. */
readonly limit: number;
/** Window length in ms. Windows are fixed and aligned to the clock. */
readonly windowMs: number;
/** Separates the counters of different limits. Default `'default'`. */
readonly name?: string;
/**
* The counter key. Default: `ctx.clientIp`, the socket peer, or the first `X-Forwarded-For` hop
* only when the server's `trustProxy` trusts that peer (configure it on `http()`, not here).
*/
readonly key?: (ctx: HttpContext) => string;
Exported from packages/security/src/contracts.ts
SecurityOptions
Interface
Options of the `security()` plugin. Each part is optional.
interface SecurityOptions {
/** CSRF guard defaults for the named `csrf` middleware. */
readonly csrf?: CsrfOptions;
/** CORS policy for the named `cors` middleware. Without it, no origin is allowed. */
readonly cors?: CorsOptions;
/** Registers the named `rateLimit` middleware and requires the `@samveys/cache` plugin. */
readonly rateLimit?: RateLimitOptions;
}
Exported from packages/security/src/contracts.ts