56 lines
1.9 KiB
JavaScript
56 lines
1.9 KiB
JavaScript
// shared/auth/requireAuth.js
|
|
import jwt from 'jsonwebtoken';
|
|
import pool from '../config/mysqlPool.js';
|
|
|
|
const { JWT_SECRET, TOKEN_MAX_AGE_MS, ACCESS_COOKIE_NAME = 'aptiva_access' } = process.env;
|
|
const MAX_AGE = Number(TOKEN_MAX_AGE_MS || 0);
|
|
|
|
function extractBearer(authz) {
|
|
if (!authz || typeof authz !== 'string') return '';
|
|
if (!authz.toLowerCase().startsWith('bearer ')) return '';
|
|
const v = authz.slice(7).trim();
|
|
if (!v || v === 'null' || v === 'undefined') return '';
|
|
return v;
|
|
}
|
|
|
|
export async function requireAuth(req, res, next) {
|
|
try {
|
|
const cookieToken = req.cookies?.[ACCESS_COOKIE_NAME];
|
|
const bearerToken = extractBearer(req.headers.authorization);
|
|
const token = cookieToken || bearerToken; // cookie always wins
|
|
|
|
if (!token) return res.status(401).json({ error: 'Auth required' });
|
|
|
|
let payload;
|
|
try { payload = jwt.verify(token, JWT_SECRET); }
|
|
catch { return res.status(401).json({ error: 'Invalid or expired token' }); }
|
|
|
|
const userId = payload.sub || payload.id || payload.userId;
|
|
const iatMs = (payload.iat || 0) * 1000;
|
|
|
|
if (MAX_AGE && Date.now() - iatMs > MAX_AGE) {
|
|
return res.status(401).json({ error: 'Session expired. Please sign in again.' });
|
|
}
|
|
|
|
const [rows] = await (pool.raw || pool).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.user = (payload && typeof payload === 'object')
|
|
? { ...payload, id: userId }
|
|
: { id: userId };
|
|
|
|
req.userId = userId;
|
|
return next(); // ✅ only once
|
|
} catch (e) {
|
|
console.error('[requireAuth]', e?.message || e);
|
|
return res.status(500).json({ error: 'Server error' });
|
|
}
|
|
}
|
|
|