File size: 2,093 Bytes
94ad3aa 4ec305b 94ad3aa 4ec305b 94ad3aa 4ec305b b6ee67e 4ec305b b6ee67e 4ec305b b6ee67e 4ec305b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /**
* Helpers para firmar y verificar tokens JWT.
*
* Responsabilidades:
* - signToken(payload) β firma un token HS256 con expiracion configurable.
* Incluye claim `jti` (UUID v4) para poder invalidarlo.
* - verifyToken(token) β verifica firma, expiracion y algoritmo (solo HS256).
* - addToDenylist(jti, exp) β anota el jti como invalidado hasta su expiracion.
* - isBlocked(jti) β true si el jti esta en la denylist.
* - pruneExpiredDenylist() β elimina entradas ya expiradas (llamado en cada logout).
*
* Denylist:
* - En memoria (Map<jti, expEpochMs>). Sin persistencia entre reinicios.
* - Aceptable porque el TTL del token es 1h; al reiniciar el servidor
* cualquier token antiguo expirara por si solo antes de que importe.
*
* Consumido por:
* - auth.service.js β signToken al hacer login; addToDenylist al hacer logout.
* - requireAuth.js β verifyToken + isBlocked en cada peticion protegida.
*
* Configuracion:
* - JWT_SECRET : minimo 32 chars (validado en config.js).
* - JWT_EXPIRES_IN: default '1h'.
*/
import { randomUUID } from 'node:crypto';
import jwt from 'jsonwebtoken';
import { config } from '../config.js';
/** @type {Map<string, number>} jti β expiry timestamp (ms) */
const denylist = new Map();
export const signToken = (payload) =>
jwt.sign({ ...payload, jti: randomUUID() }, config.JWT_SECRET, {
algorithm: 'HS256',
expiresIn: config.JWT_EXPIRES_IN,
});
export const verifyToken = (token) =>
jwt.verify(token, config.JWT_SECRET, { algorithms: ['HS256'] });
export const addToDenylist = (jti, exp) => {
pruneExpiredDenylist();
denylist.set(jti, exp * 1000);
};
export const isBlocked = (jti) => {
const exp = denylist.get(jti);
if (exp === undefined) return false;
if (Date.now() > exp) { denylist.delete(jti); return false; }
return true;
};
export const pruneExpiredDenylist = () => {
const now = Date.now();
for (const [jti, exp] of denylist) {
if (now > exp) denylist.delete(jti);
}
};
|