Appearance
Deployment
This guide covers production settings for applications using zero-auth.
Production checklist
zero-auth does not provision a database, Redis, TLS certificate, process manager, or user store. Those remain deployment and application decisions.
- Set separate, random secrets for access and refresh tokens.
- Never use the development fallback secrets from the example app.
- Use HTTPS in production.
- Set cookie
secure: truewhen using HTTP-only cookies. - Set
NODE_ENV=production. - Use a shared refresh-token store when running multiple instances.
- Mount
auth.csrf()for cookie-authenticated state-changing requests. - Allow only trusted CORS origins when browsers call the API cross-origin.
- Rate-limit login, registration, refresh, and password-reset endpoints.
- Do not log tokens, secrets, or complete cookie headers.
- Add a health check that verifies dependencies required by the app.
Environment variables
Keep secrets outside the repository:
bash
NODE_ENV=production
JWT_ACCESS_SECRET=<random-access-secret-at-least-32-characters>
JWT_REFRESH_SECRET=<different-random-refresh-secret-at-least-32-characters>Read them when creating the auth instance:
ts
const auth = createAuth({
accessSecret: process.env.JWT_ACCESS_SECRET!,
refreshSecret: process.env.JWT_REFRESH_SECRET!,
});Your platform should provide these values through its secret manager or protected environment settings.
Generate values with Node.js:
bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Never use the same secret for access and refresh tokens. Changing either secret invalidates tokens signed with the old value, so plan a coordinated sign-in or key-migration strategy.
Express behind a reverse proxy
If TLS terminates at a reverse proxy or load balancer, configure Express to trust only the proxy hops that you control:
ts
app.set("trust proxy", 1);HTTP-only cookies
Use secure cookie settings in production:
ts
const auth = createAuth({
accessSecret: process.env.JWT_ACCESS_SECRET!,
refreshSecret: process.env.JWT_REFRESH_SECRET!,
cookies: {
options: {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
},
},
});For a browser frontend on another origin, configure CORS and send credentials. Mount CSRF protection before state-changing routes:
ts
app.use(auth.csrf());
app.get("/auth/csrf-token", (_req, res) => {
res.json({ csrfToken: auth.csrfToken(res) });
});The browser must request the token with credentials and send it in the configured header on POST, PUT, PATCH, and DELETE requests.
Use sameSite: "none" only when the cross-site flow requires it, together with secure: true.
Multiple instances
Access-token verification is stateless. Refresh-token rotation is not: consumeRefreshToken needs an atomic operation in a shared store such as Redis or a database when requests can reach different instances. The legacy isRevoked + revokeRefreshToken pair remains supported with a warning but is not concurrency-safe.
The in-memory revocation store is suitable for local development and single- process tests, not for horizontally scaled production deployments.
Health checks and shutdown
Expose a health endpoint that checks the dependencies required to serve requests. The cookie/Redis example uses GET /healthz and returns 503 when Redis is unavailable. Keep liveness and readiness separate if your platform supports both.
On SIGTERM, stop accepting new traffic, finish in-flight requests, close the Redis connection, and then exit. Configure the platform's graceful-shutdown timeout to exceed the expected request duration.
Deployment sequence
- Store secrets and production configuration in the platform secret manager.
- Run database migrations and verify the refresh-token store is reachable.
- Deploy the API with HTTPS and the correct proxy trust configuration.
- Verify health, login, protected routes, refresh, CSRF, and logout.
- Roll out gradually while monitoring authentication failures and refresh reuse events.
- Confirm the previous deployment can be rolled back without losing required revocation state.
For a complete Docker and Redis reference, use the cookie + Redis example.