Production Checklist
Items to tick before sending traffic.
Run through this before your first production deploy.
Secrets
-
session.jwt.secretis set (32+ chars / 256+ bits), in a secrets manager, not in.envchecked to git. The library fails closed — it has no default and throws at boot on a missing/known-weak secret. - Refresh-token signing material is separate from
JWT_SECRET(or use the same with secret rotation in mind). - Rotation plan documented: how to roll the secret without logging everyone out simultaneously.
- OAuth client secrets (Google, Facebook, Apple, GitHub) live in the secrets manager, not in code.
- If the admin console is enabled:
adminConsole.secretKeyand a dedicatedadminConsole.sessionSecretare set (each 32+ chars — boot fails closed below that), and rotated when an operator leaves. Using onlysecretKeystill works (the signing key is derived from it), but a dedicatedsessionSecretis strongly recommended.
TLS
- Backend serves HTTPS only.
- HSTS header enabled for the auth host.
-
cookieOptions.secure: true(the default) — never serve auth cookies over plain HTTP.
Cookies
For cookie-mode sessions:
-
sameSite: 'lax'(default) — strict if your app never embeds itself, none if cross-site is intentional. -
httpOnly: true(default). -
domainis explicitly set in production (don't rely on the request host). - CORS
Access-Control-Allow-Credentials: trueand explicit origin allowlist (no*).
CORS
- Required headers in
allowedHeaders:Content-Type,Authorization,x-access-token-type, the configuredtrustDeviceHeaderName(defaultnest_auth_device_trust). - Preflight (
OPTIONS) responses cached withAccess-Control-Max-Age. - If using cookie mode: explicit
originallowlist;credentials: true.
See CORS & Security.
Rate limiting
- Enable the built-in
security.rateLimit(and/orsecurity.lockout) — or@nestjs/throttler— on/auth/login,/auth/passwordless/send,/auth/forgot-password,/auth/mfa/challenge. - Per-IP and per-identifier (so an attacker hitting many usernames from one IP is blocked). The built-in default is
keyBy: 'both'. - Admin console brute-force protection is on by default (
adminConsole.bruteForce) — leave it on. For multi-instance, backsecurity.rateLimit.storewith a shared store.
See Rate Limiting.
Sessions
-
session.storageTypeis notMEMORY— pick DATABASE or REDIS. - If multi-instance: REDIS (or sticky sessions if you really must, but DATABASE is cleaner).
-
session.maxSessionsPerUserset to a reasonable cap (default 10). - Periodic cleanup of expired DB sessions if using
DATABASE. - Refresh tokens rotated on every refresh (the default).
MFA
-
mfa.required: truefor sensitive endpoints (or for all logins in compliance-heavy apps). - Recovery codes documented in your user-facing UX.
- Trusted-device duration matches your policy (
trustedDeviceDuration: '14d'or shorter).
Email & SMS
- All six lifecycle events have a listener that delivers (or queues) the message.
- No plaintext codes in logs, error reports, or third-party tools.
- Async delivery via queue, not inline
await, so a slow provider can't DoS your auth flow.
See Sending Emails / Sending SMS.
Audit
-
audit.enabled: true. -
audit.onEventwrites to your durable audit store (not just logs). - Tamper-evident logging if your compliance regime requires it.
Database
-
synchronize: false— schema managed by migrations. - Indexes on
nest_auth_users.email,nest_auth_users.phone,nest_auth_sessions.userId,nest_auth_identities.(provider, providerId). - CASCADE delete from
NestAuthUseris configured (it ships that way; verify it survived your migration). - Backups include all 14 auth tables.
Application
-
cookieParser()middleware is registered before routes. -
ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true })is registered globally. -
AuthExceptionFilteris registered globally so error responses are structured. -
EventEmitterModule.forRoot()is inAppModuleimports — without it, every event silently no-ops. - Helmet / compression configured.
See Setup Checklist.
Monitoring
- Auth error rate dashboard.
- Login latency p95 / p99 dashboard.
- Alert on
INVALID_CREDENTIALSrate spikes (credential-stuffing). - Alert on session-store error rate.
Incident response
- Documented runbook for "leaked JWT secret" (rotate secret → all sessions invalid).
- Documented runbook for "compromised admin user" (force logout-all → reset MFA → audit).
- Tested ability to disable auth methods quickly (e.g. flip
emailAuth.enabled: falseif a CVE hits an OAuth provider).