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:
@@ -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.',
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user