dev1/backend/shared/requireAuth.js
Josh 5838f782e7
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
removed files from tracking, dependencies, fixed encryption
2025-08-19 12:24:54 +00:00

73 lines
2.2 KiB
JavaScript

// shared/auth/requireAuth.js
import jwt from 'jsonwebtoken';
import pool from '../config/mysqlPool.js';
const {
JWT_SECRET,
TOKEN_MAX_AGE_MS,
SESSION_COOKIE_NAME
} = process.env;
const MAX_AGE = Number(TOKEN_MAX_AGE_MS || 0); // 0 = disabled
const COOKIE_NAME = SESSION_COOKIE_NAME || 'aptiva_session';
// Fallback cookie parser if cookie-parser middleware isn't present
function readSessionCookie(req) {
// Prefer cookie-parser, if installed
if (req.cookies && req.cookies[COOKIE_NAME]) return req.cookies[COOKIE_NAME];
// Manual parse from header
const raw = req.headers.cookie || '';
for (const part of raw.split(';')) {
const [k, ...rest] = part.trim().split('=');
if (k === COOKIE_NAME) return decodeURIComponent(rest.join('='));
}
return null;
}
export async function requireAuth(req, res, next) {
try {
// 1) Try Bearer (legacy) then cookie (current)
const authz = req.headers.authorization || '';
let token =
authz.startsWith('Bearer ')
? authz.slice(7)
: readSessionCookie(req);
if (!token) return res.status(401).json({ error: 'Auth required' });
// 2) Verify JWT
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 (optional)
if (MAX_AGE && Date.now() - iatMs > MAX_AGE) {
return res.status(401).json({ error: 'Session expired. Please sign in again.' });
}
// 4) Invalidate tokens issued before last password change
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]
);
const changedAtMs = rows?.[0]?.password_changed_at
? new Date(rows[0].password_changed_at).getTime()
: 0;
if (changedAtMs && iatMs < changedAtMs) {
return res.status(401).json({ error: 'Session invalidated. Please sign in again.' });
}
req.userId = userId;
next();
} catch (e) {
console.error('[requireAuth]', e?.message || e);
return res.status(500).json({ error: 'Server error' });
}
}