Skip to main content

Security Model

Defense-in-depth security for the V5 Gateway.

Authentication layers

Tenant auth (hot path)

  • Bearer token hashed with SHA-256
  • Compared against KV via timing-safe equality
  • Suspended tenants rejected immediately

Admin auth

  • x-admin-key header with SHA-256 hash comparison
  • No CF Access header trust (removed 2026-04-09 — headers are trivially forgeable without CF Access proxy)
  • Admin routes restricted to gateway origin via CORS

OAuth 2.1 (MCP clients)

  • Dynamic Client Registration with 90-day TTL and IP rate limiting (5/hr)
  • PKCE mandatory — only S256, plain method rejected
  • Auth codes are single-use with 60s TTL
  • Access tokens valid for 90 days

CORS policy

Route groupAllowed origins
/api/*, /mcp/*, /health/*Any (API clients don't use CORS)
/admin/*Gateway origin only
/oauth/*Known MCP consumers (Claude, ChatGPT, Cursor)

Input validation

  • All admin endpoints validate with Zod schemas (.strict() rejects unknown fields)
  • MCP tools use Zod with .strict() — unknown parameters rejected
  • Recipe parameters validated with Zod schemas
  • Request body size limited to 1MB
  • API path length limited to 2000 characters
  • Prompt injection patterns blocked on call_api body

Token security

  • Tokens stored in KV with no TTL (managed by DO alarms)
  • Refresh tokens stored only in DO SQLite storage (never in KV)
  • Access tokens in KV contain no secrets — only the bearer token needed for API calls
  • Admin API key stored as SHA-256 hash in Wrangler secrets
  • OAuth state parameter signed with HMAC

Rate limiting

  • Global: 30 requests/minute per IP
  • DCR: 5 registrations/hour per IP
  • Admin CORS: restricted to single origin

Pre-commit security gate

Check 16 (scripts/checks/16-security-hardening.ts) blocks commits that:

  • Trust cf-access-authenticated-user-email without JWT validation
  • Use wildcard CORS (cors() without origin restriction)
  • Make fetch calls without AbortController timeout