From 073c010d72a19fe5d9472cd1fc481fd62ffd9b40 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Jul 2026 00:55:07 -0500 Subject: [PATCH] Fail fast when JWT_SECRET is missing in production (#14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- server/src/utils/auth.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/server/src/utils/auth.js b/server/src/utils/auth.js index f8f7c62..5b6aa40 100644 --- a/server/src/utils/auth.js +++ b/server/src/utils/auth.js @@ -4,16 +4,27 @@ require('dotenv').config() const log = require('./logger')('auth') const users = require('../model/users/users.model') -const JWT_SECRET = process.env.JWT_SECRET const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1d' const COOKIE_NAME = process.env.COOKIE_NAME || 'uomm_token' // Lifetime of the short-lived "password verified, awaiting TOTP" token. const TOTP_CHALLENGE_TTL = process.env.TOTP_CHALLENGE_TTL || '5m' -if (!JWT_SECRET) { - log.warn('JWT_SECRET is not set — set it in .env before going to production') +// Resolve the signing secret. Without one, jwt.sign/verify can't produce or +// validate a usable token, so every login is silently broken. Fail fast in +// production rather than booting into that state; in dev fall back to a known +// insecure secret so login still works locally (with a loud warning). +function resolveJwtSecret() { + const secret = process.env.JWT_SECRET + if (secret) return secret + if (process.env.NODE_ENV === 'production') { + throw new Error('JWT_SECRET must be set in production') + } + log.warn('JWT_SECRET is not set — using an insecure development fallback. Set JWT_SECRET in .env before deploying.') + return 'dev-insecure-jwt-secret-do-not-use-in-production' } +const JWT_SECRET = resolveJwtSecret() + function signToken(user) { const payload = { id: user.id, username: user.username, role: user.role } return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }) -- 2.49.1