Skip to content

Security

Handoff checklist for platform and security reviewers. Implementation lives under apps/server/src/ (middleware, routes, env.ts).

Production boot guards

When NODE_ENV=production, the server refuses to start unless:

RequirementWhy
REQUIRE_JWT_EXP=1Reject session JWTs without exp
REQUIRE_S3=1Durable uploads (no ephemeral container disk)
Non-empty CORS_ORIGINSExplicit browser allowlist
JWT_SECRET ≥ 32 charsClient session signing
ADMIN_SESSION_SECRET ≥ 32, ≠ JWTAdmin session signing
FILE_URL_SECRET ≥ 32, distinctPublic file URL HMAC
No custom INTERCOM_BASE_URLPrevents token exfiltration via API override

See assertProductionSecurity() in apps/server/src/env.ts.

Client auth (SDK)

  • HS256 JWT in Authorization: Bearer.
  • Claim: userId or sub.
  • Mint only on the host backend with the shared JWT_SECRET.
  • Resource routes check the contact owns the conversation / ticket / file (IDOR → 403). Covered by server tests.

Admin auth

  • Password verified against admin_users (scrypt).
  • Session = HMAC token (ADMIN_SESSION_SECRET); deactivated users fail verification.
  • Login lockout by IP (in-memory) — configure TRUST_PROXY_HOPS behind proxies.
  • Permission gates on mutating admin routes.

Files

  • Upload allow-list by magic bytes (JPEG/PNG/GIF/WEBP/BMP/PDF/MP4/TXT), size caps (MAX_FILE_MB / image / video).
  • Authenticated GET /api/files/:id enforces ownership.
  • Public GET /files/:id?t= requires a key-bound HMAC (FILE_URL_SECRET). Treat leaked URLs as temporary bearers.

Webhooks

  • POST /webhooks/intercom verifies X-Hub-Signature (SHA1) with INTERCOM_CLIENT_SECRET.
  • Events deduped in processed_events.

Content safety

  • Operator HTML → typed rich-text tree in the SDK (no dangerouslySetInnerHTML in chat UI); URL schemes allow-listed.
  • Channel comments: no links, DLP (seed / key / card / password) with redacted moderation events.

Devtools

/api/devtools (contact wipe) mounts only when ENABLE_DEVTOOLS=1 andNODE_ENV !== "production". Bulk wipe also needs ENABLE_DEVTOOLS_RESET_ALL=1. Leave both empty on shared stands and production.

Secrets rotation

SecretEffect of rotate
JWT_SECRETAll client session tokens invalid
ADMIN_SESSION_SECRETAll admin sessions invalid
FILE_URL_SECRETExisting public file links fail verify
Intercom / S3 / FCM / TelegramUpdate env + redeploy; rotate at provider

Logging

Message bodies, raw files, and seed-like secrets must not appear in server logs. Diagnostics accept device metadata only.

AW Chat SDK — integration & platform handoff docs.