first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
# `csrf-policy` — `CsrfPolicy.requiresCsrf`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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:
```ts
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.