Files
skills/docs/api-stack/api/api-auth/src/csrf-policy/csrf-policy.md
2026-05-29 08:20:56 +01:00

3.2 KiB

csrf-policy — CsrfPolicy.requiresCsrf

(no submodules)

Purpose

Exposes a single pure predicate, CsrfPolicy.requiresCsrf(scheme), that defaults CSRF protection ON for any cookie-based scheme and OFF for any scheme where the caller must actively attach the credential. This default codifies the design-doc §6 rule "Cookie-based auth adapters trigger CSRF verification" so every concrete adapter inherits the right behaviour without restating it.

The package exposes only the default; the host (api-host) applies per-route csrf: false overrides on top. Keeping the default and the override separate means a route-level opt-out is always auditable as an explicit decision in the manifest, never a side-effect of which adapter happened to be wired.

Features

  • Pure function — no side effects, no module-load cost.
  • Frozen container object so test code can expect(CsrfPolicy) reliably and consumers cannot monkey-patch the predicate.
  • Total over the OpenApiSecurityScheme discriminated union — no default branch means an added scheme type at the schema layer will surface a type-level reminder to revisit the policy.

Usage: Basic

import { CsrfPolicy } from "@suaveplan/api-auth/csrf-policy";

const needsCsrf = CsrfPolicy.requiresCsrf({
    type: "apiKey",
    in: "cookie",
    name: "sp_session",
}); // → true

Usage: Advanced

Drive a host-side CSRF middleware off the policy, with a per-route override applied at the call site:

import { CsrfPolicy } from "@suaveplan/api-auth/csrf-policy";
import type { OpenApiSecurityScheme } from "@suaveplan/api-auth/security-scheme";

interface RouteCsrfDecision {
    readonly required: boolean;
    readonly reason: "scheme-default" | "route-override";
}

function decideCsrf(
    scheme: OpenApiSecurityScheme,
    routeOverride: boolean | undefined,
): RouteCsrfDecision {
    if (routeOverride !== undefined) {
        return { required: routeOverride, reason: "route-override" };
    }
    return {
        required: CsrfPolicy.requiresCsrf(scheme),
        reason: "scheme-default",
    };
}

const decision = decideCsrf(
    { type: "http", scheme: "bearer" },
    undefined,
);
// → { required: false, reason: "scheme-default" }

API Reference

Symbol Kind Description
CsrfPolicy.requiresCsrf function (scheme) => boolean. Cookie schemes → true.

Implementation Notes

  • The predicate inspects type === "apiKey" && in === "cookie" — apiKey-in- header is treated identically to Authorization: Bearer … for CSRF purposes because the caller must attach it explicitly.
  • oauth2 and openIdConnect default OFF — both are bearer-token-derived in practice; if a deployment exposes an oauth2 access token via cookie, that's a custom apiKey-in-cookie scheme, not the oauth2 scheme.
  • Object.freeze guards the predicate slot; the predicate itself is a regular method and TypeScript prevents reassignment via the Readonly inference.