// backend/shared/requireAuth.js import jwt from 'jsonwebtoken'; import pool from '../config/mysqlPool.js'; function readSessionCookie(req, cookieName) { if (req.cookies && req.cookies[cookieName]) return req.cookies[cookieName]; const raw = req.headers.cookie || ''; for (const part of raw.split(';')) { const [k, ...rest] = part.trim().split('='); if (k === cookieName) return decodeURIComponent(rest.join('=')); } return null; } function toMs(v) { if (v == null) return 0; const n = typeof v === 'number' ? v : parseInt(String(v), 10); if (!Number.isFinite(n) || Number.isNaN(n)) return 0; return n < 1e12 ? n * 1000 : n; // convert seconds to ms if small } export async function requireAuth(req, res, next) { try { const JWT_SECRET = process.env.JWT_SECRET; const COOKIE_NAME = process.env.SESSION_COOKIE_NAME || 'aptiva_session'; const MAX_AGE = Number(process.env.TOKEN_MAX_AGE_MS || 0); if (!JWT_SECRET) { console.error('[requireAuth] JWT_SECRET missing'); return res.status(500).json({ error: 'Server misconfig' }); } // 1) Grab token const authz = req.headers.authorization || ''; const token = authz.startsWith('Bearer ') ? authz.slice(7) : readSessionCookie(req, COOKIE_NAME); if (!token) return res.status(401).json({ error: 'Auth required' }); // 2) Verify let payload; try { payload = jwt.verify(token, JWT_SECRET); } catch { return res.status(401).json({ error: 'Invalid or expired token' }); } const userId = payload.id; const iatMs = (payload.iat || 0) * 1000; // 3) Absolute max token age if (MAX_AGE && Date.now() - iatMs > MAX_AGE) { return res.status(401).json({ error: 'Session expired. Please sign in again.' }); } // 4) Password change invalidation let changedAtMs = 0; try { const sql = pool.raw || pool; const [rows] = await sql.query( 'SELECT password_changed_at FROM user_auth WHERE user_id = ? ORDER BY id DESC LIMIT 1', [userId] ); changedAtMs = toMs(rows?.[0]?.password_changed_at); } catch (e) { console.warn('[requireAuth] password_changed_at check skipped:', e?.message || e); } if (changedAtMs && iatMs < changedAtMs) { return res.status(401).json({ error: 'Session invalidated. Please sign in again.' }); } req.userId = userId; return next(); } catch (e) { console.error('[requireAuth]', e?.message || e); return res.status(500).json({ error: 'Server error' }); } }