Fail fast when JWT_SECRET is missing in production (closes #14) #21
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/jwt-secret-fail-fast"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Fixes #14 - the server previously booted with no
JWT_SECRET, only logging a warning. Without a secret,jwt.sign/jwt.verifycan't produce or validate a usable token, so every login silently fails while the process still reports healthy - and running a production instance with no configured secret is a real safety hazard.Change
server/src/utils/auth.jsnow resolves the secret through a smallresolveJwtSecret()helper instead of just warning:NODE_ENV=production) ? throwJWT_SECRET must be set in production. The process refuses to start rather than booting into an unusable, unsafe state (fail fast).log.warntelling you to setJWT_SECRETbefore deploying.This keeps the friction-free local dev experience (no
.envrequired to click around) while making a misconfigured production deploy impossible to miss.Why this approach
The issue offered "fail fast in prod" or "always throw". I went with fail-fast-in-prod + explicit dev fallback because:
jwt.verifythrew on every request - login was silently broken even locally. The old warning didn't fix that; a real fallback value does.throw, which is the case the issue actually cares about.Testing
node --check server/src/utils/auth.js- passes.JWT_SECRETset ? used as-is (unchanged behavior).NODE_ENV=production? throws at startup.Notes
server/.env.examplealready shipsJWT_SECRET=dev-only-change-me, so the documented dev setup is unaffected.?? Generated with Claude Code
auth.js previously only logged a warning when JWT_SECRET was unset and then continued to boot. With no secret, jwt.sign/jwt.verify cannot produce or validate a usable token, so every login silently fails while the server appears healthy — and booting a production instance without a configured secret is a safety hazard. Resolve the secret through resolveJwtSecret(): - production (NODE_ENV=production): throw, so the process refuses to start without a real secret instead of running unusable. - dev/other: fall back to a known insecure secret so local login keeps working, with a loud warning to set JWT_SECRET before deploying. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>