fix(auth): enforce JWT secrets in all environments, not just production

validateEnv() previously skipped validation entirely when NODE_ENV !== 'production',
allowing the app to start without JWT_SECRET and JWT_REFRESH_SECRET in dev/staging.
Split required vars into ALWAYS_REQUIRED (JWT secrets) and REQUIRED_IN_PRODUCTION
(infrastructure) so security-critical secrets are validated in every environment.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 22:44:19 +07:00
parent 0abf9df84e
commit 8a86cf42d4

View File

@@ -1,11 +1,16 @@
/**
* Validates that critical environment variables are set in production.
* Call this at application startup before any module initialization.
* Validates that critical environment variables are set at application startup.
*
* Security-sensitive vars (JWT secrets) are required in ALL environments.
* Infrastructure vars (DATABASE_URL, CORS, Redis) are required only in production.
*/
const REQUIRED_IN_PRODUCTION: readonly string[] = [
const ALWAYS_REQUIRED: readonly string[] = [
'JWT_SECRET',
'JWT_REFRESH_SECRET',
];
const REQUIRED_IN_PRODUCTION: readonly string[] = [
'DATABASE_URL',
'CORS_ORIGINS',
'REDIS_HOST',
@@ -30,14 +35,11 @@ const REQUIRED_WHEN_USED: ReadonlyMap<string, string> = new Map([
export function validateEnv(): void {
const isProduction = process.env['NODE_ENV'] === 'production';
if (!isProduction) {
return;
}
const missing: string[] = [];
for (const key of REQUIRED_IN_PRODUCTION) {
// JWT secrets are required in every environment — a missing secret is a
// security risk regardless of NODE_ENV.
for (const key of ALWAYS_REQUIRED) {
if (!process.env[key]) {
missing.push(key);
}
@@ -45,7 +47,26 @@ export function validateEnv(): void {
if (missing.length > 0) {
throw new Error(
`Missing required environment variables in production:\n ${missing.join('\n ')}\n` +
`Missing required environment variables:\n ${missing.join('\n ')}\n` +
'JWT_SECRET and JWT_REFRESH_SECRET must always be set. See .env.example.',
);
}
if (!isProduction) {
return;
}
// Infrastructure vars — required in production only.
const missingProd: string[] = [];
for (const key of REQUIRED_IN_PRODUCTION) {
if (!process.env[key]) {
missingProd.push(key);
}
}
if (missingProd.length > 0) {
throw new Error(
`Missing required environment variables in production:\n ${missingProd.join('\n ')}\n` +
'See .env.example for the full list of variables.',
);
}