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
JWT_AUD setReject tokens without the expected audience (e.g. aw-chat)
JWT_ISS setReject tokens without the expected issuer
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.

Opt-in hardening (default off, so enabling can't surprise a not-yet-ready deploy): REQUIRE_DB_VERIFY_FULL=1 asserts sslmode=verify-full, REQUIRE_S3_HTTPS=1 asserts an https S3 endpoint. Plus JWT_MAX_AGE, SKIP_MIGRATIONS, REQUIRE_MEDIA_HTTPS / SAFE_MEDIA_HOSTS, and DEVTOOLS_RESET_TOKEN. Full list and the review they close: Server review remediation.

Client auth (SDK) ​

  • HS256 JWT in Authorization: Bearer.
  • Claim: userId or sub; production also requires exp, and when configured aud / iss (JWT_AUD / JWT_ISS). Optional jti is accepted (no revoke store in this service — short TTL + host mint controls).
  • Mint only on the host backend with the shared JWT_SECRET.
  • Resource routes check the contact owns the conversation / ticket / file (IDOR → 403). CSAT/complaint link ids and device DELETE are covered the same way. Covered by server tests.

Host mint contract (required outside this repo) ​

Sign HS256 with the same secret as chat JWT_SECRET. Claims:

ClaimValue
userId or subHost user id
expRequired (TTL ≤ 1h recommended)
audMust match chat JWT_AUD (e.g. aw-chat)
issMust match chat JWT_ISS

Chat host env: REQUIRE_JWT_EXP=1, JWT_AUD, JWT_ISS. Full AW / rn-app / infra checklist: SEC-48 handoff. Edge notes also in Deployment.

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.