From 9f6f1f46853038f7b11a7b3243de412dbf3ccad7 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 05:13:33 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20hardcoded=20JWT=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed hardcoded 'your-secret-key-change-this' fallback in `src/lib/auth.ts`. - Throws an error in production if NEXTAUTH_SECRET is not set (fails securely). - Generates a crypto-secure random byte string as a fallback in development environments. - Recorded a critical learning in `.jules/sentinel.md`. Co-authored-by: GerryK97 <210032986+GerryK97@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ src/lib/auth.ts | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..a7ca9123 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-05-22 - Hardcoded JWT Secret Fallback Removed +**Vulnerability:** A hardcoded string (`'your-secret-key-change-this'`) was used as a fallback for `JWT_SECRET` in `src/lib/auth.ts`. +**Learning:** This existed to prevent crashes in development environments where `NEXTAUTH_SECRET` might not be set. However, it creates a critical vulnerability where tokens could be predictably forged in production if the environment variable is accidentally missing or misconfigured. +**Prevention:** Authentication mechanisms must "fail securely." In production, if critical secrets are missing, the application should throw a fatal error rather than silently falling back to insecure defaults. In development, generate a cryptographically strong random string (e.g., using `crypto.randomBytes`) so that the application still works but remains secure (even though tokens will be invalidated on restart). diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 9a936ce5..fa43622f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -2,8 +2,21 @@ import bcrypt from 'bcrypt'; import jwt from 'jsonwebtoken'; import { IUser } from '@/models/User'; import { NextRequest } from 'next/server'; +import crypto from 'crypto'; + +// Security Fix: Prevent use of hardcoded secret key for JWT signing. +// If NEXTAUTH_SECRET is not provided, we fail securely in production. +// In development, we use a strong random byte string so tokens are secure (though they invalidate on restart). +let JWT_SECRET = process.env.NEXTAUTH_SECRET; +if (!JWT_SECRET) { + if (process.env.NODE_ENV === 'production') { + throw new Error('CRITICAL SECURITY ERROR: NEXTAUTH_SECRET environment variable is not set.'); + } else { + console.warn('WARNING: NEXTAUTH_SECRET is not set. Generating a temporary random secret for development.'); + JWT_SECRET = crypto.randomBytes(32).toString('hex'); + } +} -const JWT_SECRET = process.env.NEXTAUTH_SECRET || 'your-secret-key-change-this'; const JWT_EXPIRY = '7d'; // 7 days export interface JWTPayload { @@ -79,7 +92,7 @@ export function generateToken(user: IUser): string { assignedTournaments: user.assignedTournaments || [], }; - return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRY }); + return jwt.sign(payload, JWT_SECRET as string, { expiresIn: JWT_EXPIRY }); } /** @@ -87,7 +100,7 @@ export function generateToken(user: IUser): string { */ export function verifyToken(token: string): JWTPayload | null { try { - const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload; + const decoded = jwt.verify(token, JWT_SECRET as string) as JWTPayload; return decoded; } catch (error) { return null;