import jwt, { SignOptions, VerifyOptions } from 'jsonwebtoken'; import config from '../config'; import logger from './logger'; import type { JwtPayload } from '../types'; interface TokenPayload { userId?: string; email?: string; roleId?: string; roleName?: string; id?: string; role?: string; [key: string]: unknown; } /** * Generate an access token * @param payload - Data to encode in the token * @returns Signed JWT access token */ export const generateAccessToken = (payload: TokenPayload): string => { const options: SignOptions = { expiresIn: config.jwt.accessTokenExpiresIn as SignOptions['expiresIn'], algorithm: 'HS256', }; try { const token = jwt.sign(payload, config.jwt.accessTokenSecret, options); return token; } catch (error) { logger.error('Error generating access token', { error: error instanceof Error ? error.message : 'Unknown error', }); throw new Error('Failed to generate access token'); } }; /** * Generate a refresh token * @param payload - Data to encode in the token * @returns Signed JWT refresh token */ export const generateRefreshToken = (payload: TokenPayload): string => { const options: SignOptions = { expiresIn: config.jwt.refreshTokenExpiresIn as SignOptions['expiresIn'], algorithm: 'HS256', }; try { const token = jwt.sign(payload, config.jwt.refreshTokenSecret, options); return token; } catch (error) { logger.error('Error generating refresh token', { error: error instanceof Error ? error.message : 'Unknown error', }); throw new Error('Failed to generate refresh token'); } }; /** * Verify an access token * @param token - JWT access token to verify * @returns Decoded payload if valid, null if invalid or expired */ export const verifyAccessToken = (token: string): any => { const options: VerifyOptions = { algorithms: ['HS256'], }; try { const decoded = jwt.verify( token, config.jwt.accessTokenSecret, options ); return decoded; } catch (error) { if (error instanceof jwt.TokenExpiredError) { logger.debug('Access token expired'); } else if (error instanceof jwt.JsonWebTokenError) { logger.debug('Invalid access token', { error: error.message, }); } else { logger.error('Error verifying access token', { error: error instanceof Error ? error.message : 'Unknown error', }); } return null; } }; /** * Verify a refresh token * @param token - JWT refresh token to verify * @returns Decoded payload if valid, null if invalid or expired */ export const verifyRefreshToken = (token: string): any => { const options: VerifyOptions = { algorithms: ['HS256'], }; try { const decoded = jwt.verify( token, config.jwt.refreshTokenSecret, options ); return decoded; } catch (error) { if (error instanceof jwt.TokenExpiredError) { logger.debug('Refresh token expired'); } else if (error instanceof jwt.JsonWebTokenError) { logger.debug('Invalid refresh token', { error: error.message, }); } else { logger.error('Error verifying refresh token', { error: error instanceof Error ? error.message : 'Unknown error', }); } return null; } }; /** * Decode a token without verification (for debugging) * @param token - JWT token to decode * @returns Decoded payload or null */ export const decodeToken = (token: string): any => { try { const decoded = jwt.decode(token); return decoded; } catch (error) { logger.error('Error decoding token', { error: error instanceof Error ? error.message : 'Unknown error', }); return null; } };