fix(auth): centralize JWT secret + 24h TTL (Brief 1 of fix-auth-bypass)
- New `lib/auth-secret.js` is the single source of truth for `JWT_SECRET`
and the canonical `JWT_TOKEN_TTL = '24h'`. Module throws at import time
if `process.env.JWT_SECRET` is unset — no silent fallback to the literal
`'your-secret-key-change-in-production'`.
- 7 callers refactored to import from the helper:
lib/permission-middleware.js
pages/api/auth-utils.js (also drops unused `'7d'` → JWT_TOKEN_TTL)
pages/api/auth/login.js (also routes via auth-utils.generateToken)
pages/api/auth/register.js (same)
pages/api/auth/verify.js (Brief 2 still owns the no-token admin branch)
pages/api/favorites.js
pages/api/users/search.js
- `process.env.JWT_SECRET` now appears exactly once in the JS source
(lib/auth-secret.js). `your-secret-key-change-in-production` is gone.
- TTL drift reconciled: auth-utils used `'7d'`, login/register used
inline `'24h'`. Both now route through imported `JWT_TOKEN_TTL` (24h).
Pre-deploy reminder: Vercel must have `JWT_SECRET` set before merge or
serverless functions refuse to boot. Existing tokens (signed against the
fallback literal) will be invalidated — users will need to log in again.
Resolves AGENTS.md gotcha #3. Brief 2/3/4/5 still pending in convoy.
Convoy: fix-auth-bypass / Brief 1
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
bc0d1687d0
commit
5d72277355
8 changed files with 22 additions and 27 deletions
12
lib/auth-secret.js
Normal file
12
lib/auth-secret.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
|
||||
if (!JWT_SECRET) {
|
||||
throw new Error(
|
||||
'JWT_SECRET environment variable is not set. ' +
|
||||
'Set it in .env.local for local dev, or in the Vercel project settings for deploys. ' +
|
||||
'Generate a strong secret with: openssl rand -hex 32'
|
||||
);
|
||||
}
|
||||
|
||||
export { JWT_SECRET };
|
||||
export const JWT_TOKEN_TTL = '24h';
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { sql } from '@vercel/postgres';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
import { JWT_SECRET } from './auth-secret.js';
|
||||
|
||||
/**
|
||||
* Get user ID from request headers
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import jwt from 'jsonwebtoken';
|
||||
import { db } from '../../lib/database.js';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
||||
import { JWT_SECRET, JWT_TOKEN_TTL } from '../../lib/auth-secret.js';
|
||||
|
||||
export async function hashPassword(password) {
|
||||
const bcrypt = await import('bcryptjs');
|
||||
|
|
@ -21,7 +20,7 @@ export function generateToken(user) {
|
|||
role: user.role
|
||||
},
|
||||
JWT_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
{ expiresIn: JWT_TOKEN_TTL }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { sql } from '@vercel/postgres';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
import { generateToken } from '../auth-utils.js';
|
||||
|
||||
export default async function handler(req, res) {
|
||||
// Set CORS headers
|
||||
|
|
@ -48,11 +46,7 @@ export default async function handler(req, res) {
|
|||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{ userId: user.id, email: user.email, role: user.role },
|
||||
JWT_SECRET,
|
||||
{ expiresIn: '24h' }
|
||||
);
|
||||
const token = generateToken({ id: user.id, email: user.email, role: user.role });
|
||||
|
||||
// Return user data (without password) and token
|
||||
const { password: _, ...userWithoutPassword } = user;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { sql } from '@vercel/postgres';
|
||||
import { generateUniqueSlug } from '../../../lib/slug-utils.js';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
import { generateToken } from '../auth-utils.js';
|
||||
|
||||
export default async function handler(req, res) {
|
||||
// Set CORS headers
|
||||
|
|
@ -140,11 +138,7 @@ export default async function handler(req, res) {
|
|||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{ userId: user.id, email: user.email, role: user.role },
|
||||
JWT_SECRET,
|
||||
{ expiresIn: '24h' }
|
||||
);
|
||||
const token = generateToken({ id: user.id, email: user.email, role: user.role });
|
||||
|
||||
// Return user data without password
|
||||
const userResponse = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { sql } from '@vercel/postgres';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
import { JWT_SECRET } from '../../../lib/auth-secret.js';
|
||||
|
||||
export default async function handler(req, res) {
|
||||
// Set CORS headers
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { sql } from '@vercel/postgres';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
import { JWT_SECRET } from '../../lib/auth-secret.js';
|
||||
|
||||
export default async function handler(req, res) {
|
||||
// Set CORS headers
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { sql } from '@vercel/postgres';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
import { JWT_SECRET } from '../../../lib/auth-secret.js';
|
||||
|
||||
export default async function handler(req, res) {
|
||||
// Set CORS headers
|
||||
|
|
|
|||
Loading…
Reference in a new issue