diff --git a/CLAUDE.md b/CLAUDE.md index fc3b1cb..366f7fa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -160,6 +160,7 @@ bun scripts/smoke-prod.ts # READ-ONLY live checks vs production - **CORS split (#35, intentional divergence from the hub)**: wildcard-uncredentialed on `/oauth/token` + `/oauth/revoke`, reflected-Origin+credentials on `/oauth/register`, **no CORS headers at all on `/oauth/authorize`** (pinned by a negative conformance test). The hub applies echo-Origin+credentials uniformly across `/oauth/*`; the cloud split is strictly more conservative, and every wire BODY stays hub-identical. Both CORS helpers expose `WWW-Authenticate` (hub parity). Canonical doc: the posture block in `workers/identity/src/oauth-shared.ts`. - **KDF (#28, the honest version)**: workerd caps PBKDF2 at 100k iterations and ships no argon2 — 100k is the runtime's ceiling and we don't pretend otherwise. Verifiers are PBKDF2-**SHA512**@100k (versioned format; legacy sha256 hashes verify and transparently re-hash on next successful password login). The actual defenses are architectural: passwords are an **optional secondary** factor (magic-link primary), the DO rate limiter (#30) blunts online guessing, TOTP 2FA is available, and the D1 password store never crosses the wire. Full note: `workers/identity/src/users.ts`. +- **CSP (P0.2, 0.0.8-rc.53)**: EVERY server-rendered page carries a `Content-Security-Policy`, attached at the single HTML choke point `htmlResponse` (`oauth-shared.ts`) — so JSON/machine endpoints (token/JWKS/webhook) never get one. Strict ceremony policy: `default-src 'none'`; `script-src 'self' 'nonce-'` (NO `'unsafe-inline'` for script — the one inline script, the console's create-moment/clipboard/checklist JS, is admitted only by a **per-response 128-bit nonce**); `style-src 'self' 'unsafe-inline'` (the pages carry inline `