/** * Módulo de geração e validação de tokens JWT * Access tokens (15min) e Refresh tokens (7 dias) */ import jwt from "jsonwebtoken"; interface TokenPayload { userId: number; username: string; type: "access" | "refresh"; } /** * Obtém o secret do JWT (lazy loading) */ function getJWTSecret(): string { const secret = process.env.JWT_SECRET; if (!secret) { throw new Error("JWT_SECRET não está definido nas variáveis de ambiente"); } return secret; } /** * Obtém o refresh secret do JWT (lazy loading) */ function getJWTRefreshSecret(): string { const secret = process.env.JWT_REFRESH_SECRET; if (!secret) { throw new Error( "JWT_REFRESH_SECRET não está definido nas variáveis de ambiente" ); } return secret; } // Tempos de expiração const ACCESS_TOKEN_EXPIRY = "1h"; // 1 hora const REFRESH_TOKEN_EXPIRY = "7d"; // 7 dias /** * Gera access token JWT * @param userId - ID do usuário * @param username - Nome de usuário * @returns Access token */ export function generateAccessToken( userId: number, username: string ): string { const payload: TokenPayload = { userId, username, type: "access", }; return jwt.sign(payload, getJWTSecret(), { expiresIn: ACCESS_TOKEN_EXPIRY, }); } /** * Gera refresh token JWT * @param userId - ID do usuário * @param username - Nome de usuário * @returns Refresh token */ export function generateRefreshToken( userId: number, username: string ): string { const payload: TokenPayload = { userId, username, type: "refresh", }; return jwt.sign(payload, getJWTRefreshSecret(), { expiresIn: REFRESH_TOKEN_EXPIRY, }); } /** * Valida e decodifica access token * @param token - Access token * @returns Payload do token ou null se inválido */ export function verifyAccessToken(token: string): { userId: number; username: string; } | null { try { const decoded = jwt.verify(token, getJWTSecret()) as TokenPayload; if (decoded.type !== "access") { return null; } return { userId: decoded.userId, username: decoded.username, }; } catch (error) { return null; } } /** * Valida e decodifica refresh token * @param token - Refresh token * @returns Payload do token ou null se inválido */ export function verifyRefreshToken(token: string): { userId: number; username: string; } | null { try { const decoded = jwt.verify(token, getJWTRefreshSecret()) as TokenPayload; if (decoded.type !== "refresh") { return null; } return { userId: decoded.userId, username: decoded.username, }; } catch (error) { return null; } }