Skip to content

Documentation

API Security & Token Hardening

Enterprise-grade API security architecture: 4-layer defense-in-depth, bearer tokens, tenant isolation, rate limiting, CSRF, and CSP.

6/26/2026
6 min read Edit on GitHub

SveltyCMS employs a zero-trust architecture for API access — authentication alone is insufficient. Every API request passes through 4 independent security layers that re-validate permissions at each boundary.


1. Defense-in-Depth Architecture

Request → Layer 1 (Middleware) → Layer 2 (Dispatcher) → Layer 3 (Handler) → Layer 4 (Page Action) → Response
Layer What It Checks Failure Mode
Layer 1 — Middleware CSP, CORS, HSTS, X-Frame-Options, firewall patterns, body size (10MB), rate limiting, __Host- cookie prefix, CSRF 403/429 before any business logic runs
Layer 2 — Dispatcher ENDPOINT_PERMISSIONS mapping, SCIM blocking, admin fast-path 403 for any unmapped namespace
Layer 3 — Handler Admin verification for mutations, media:write/media:delete checks 403 on unauthorized mutations
Layer 4 — Page Action requireCollectionBuilderPermission(), inline RBAC checks 403 before state change

Fail-Closed Default: If a permission mapping is missing or ambiguous, the dispatcher returns 403 — never 200 with leaked data.


2. Token Architecture

2.1 Website Tokens (API Keys)

Designed for machine-to-machine communication. Stateless secrets bound to a specific tenantId and role.

Property Detail
Generation CSPRNG with rejection sampling (zero modulo bias)
Storage SHA-256 hashed before persistence — plaintext never stored
Types content-api (read/restricted), admin-api (full access)
Expiry Configurable per-token with automatic invalidation
Consumption Atomic one-time token support via dbAdapter.system.websiteTokens.consume() — prevents TOCTOU race conditions
Lookup Single-pass getByToken — no redundant round-trips

2.2 Session Tokens

Browser-based authentication uses secure session cookies:

  • Cookie prefix: __Host- in production (RFC 6265bis — prevents subdomain leakage)
  • Attributes: httpOnly, secure, sameSite: strict
  • Rotation: Every 15 minutes for active users
  • Cascading invalidation: Password change purges all sessions across all devices
  • 3-layer cache: Memory (L1) → Redis (L2) → Database (L3)

3. Authentication Flow

3.1 Bearer Token Validation

1. Extract token from Authorization: Bearer <token> header
2. Single DB query: dbAdapter.system.websiteTokens.getByToken
3. Normalize legacy tokens → default to content-api
4. Create Virtual User context (no full User document hydration)
5. Verify tenantId match → reject mismatched tokens
locals.user = {
  _id: `token:${token._id}`,
  role: token.type === "admin-api" ? "admin" : "guest",
  tenantId: token.tenantId ?? locals.tenantId,
  isApiToken: true,
};

3.2 GraphQL Authentication

GraphQL requests pass through the same middleware pipeline as REST:

  • HTTP entry: handleRequest() rejects unauthenticated requests (401) before schema execution
  • Validation plugin: Query depth limit (8), alias limit (15)
  • Introspection: Explicitly blocked in production via NoSchemaIntrospectionCustomRule
  • Subscriptions: Context handler validates sessions before WebSocket upgrade

3.3 SCIM 2.0 Authentication

SCIM endpoints (/api/scim/v2/Users, /Groups, /Bulk) use Bearer token authentication with additional tenant-scoped RBAC checks:

  • SCIM tokens require system:admin or explicit SCIM provisioning permissions
  • Bulk operations are validated for cross-tenant leakage before execution
  • PATCH operations use RFC 7644-compliant filter evaluation

4. Multi-Tenant Hardening

4.1 Orphaned Token Protection

If a token’s owner is deleted (tenantId: null), the system isolates the token to the current request’s tenant context — preventing cross-tenant privilege escalation.

4.2 Tenant Isolation Checks

Every Bearer-token request is verified against the environment’s tenantId. Mismatched tokens are rejected with 403 and an audit log event.

4.3 Batch Operation Guard

batch-module.ts rejects coalesced operations containing mixed tenant IDs — preventing cross-tenant data leakage in shared-DB deployments.


5. Rate Limiting & DoS Prevention

Mechanism Location Detail
Login brute-force handle-authentication.ts sveltekit-rate-limiter — 5 attempts → 15-min lockout (HTTP 423)
API rate limit SecurityResponseService rate-limiter-flexible with Redis-backed state, per-IP and per-tenant
Body size limit api-handler.ts 10MB cap — returns 413 PAYLOAD_TOO_LARGE before parsing
Memory load shedding hooks.server.ts Rejects mutation traffic (503) when heap > 90%
Retry-After header RFC 6585 compliant All 429 responses include Retry-After: 60

6. CSP & Security Headers

All API responses include mandatory security headers:

Header Value Purpose
Content-Security-Policy script-src 'self' (production API routes) Blocks inline script execution
Strict-Transport-Security max-age=31536000; includeSubDomains Enforces HTTPS
X-Frame-Options DENY Prevents clickjacking
X-Content-Type-Options nosniff Prevents MIME sniffing
Cross-Origin-Opener-Policy same-origin Spectre/Meltdown protection
Cross-Origin-Embedder-Policy require-corp Cross-origin isolation
Cross-Origin-Resource-Policy same-origin Resource isolation
Referrer-Policy strict-origin-when-cross-origin Referrer leakage control

GraphQL Playground CSP: The relaxed CSP (unsafe-inline, unsafe-eval) is only applied in non-production environments. In NODE_ENV=production, GraphQL endpoints use the same strict CSP as REST APIs.


7. CSRF Protection

Multi-layer origin and referer validation for all mutation requests (POST, PUT, DELETE, PATCH):

  • SvelteKit CSRF: Enabled by default for form actions
  • Origin check: All mutations verify Origin or Referer headers match the current host
  • SAML CSRF: Dual-cookie protection for SAML ACS endpoints
  • Token-bound mutations: Bearer token requests are CSRF-immune (no cookie dependency)

8. Performance Benchmarks

All metrics self-measured via bun test tests/benchmarks/ — reproducible on any machine.

Metric p95 Adapter
Bearer token validation 0.06 ms SQLite (Local SDK)
Bearer token validation 0.37 ms MariaDB
Session validation (L1 cache hit) <0.001 ms Memory
Session validation (L3 DB fallback) 1.78 ms PostgreSQL
Full auth pipeline (middleware → RBAC) 1.61 ms All adapters
API request (auth + RBAC + response) 1.16 ms SQLite
Concurrent throughput 1,054 RPS MariaDB

Reproduce: bun test tests/benchmarks/auth-performance.test.ts


9. Audit & Compliance

  • Crypto-chained logs: Every API mutation is logged with SHA-256 chained audit entries — tamper-evident verification
  • security.txt: RFC 9116 standard at /.well-known/security.txt
  • EU Directive 2006/114/EC: All competitive comparisons use verifiable public data
  • Incident response: Full runbook
  • 0 published CVEs — private reporting via GitHub Security Advisories

Related

securityauthapihardeningtokensrate-limiting
Was this page helpful?