import { SignJWT, jwtVerify, errors, JWTPayload } from 'jose'; import { jwtSecret } from './env-exporter'; // JWT Payload Types export interface UserJWTPayload extends JWTPayload { userId: number; name?: string; email?: string; mobile?: string; roles?: string[]; } export interface StaffJWTPayload extends JWTPayload { staffId: number; name: string; } // Convert string secret to Uint8Array const getSecret = () => { if (!jwtSecret) { throw new Error('JWT secret not configured'); } return new TextEncoder().encode(jwtSecret); }; /** * Sign a JWT token * Compatible with tokens signed by jsonwebtoken library */ export const signToken = async ( payload: Record, expiresIn: string = '7d' ): Promise => { const secret = getSecret(); return new SignJWT(payload) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() .setExpirationTime(expiresIn) .sign(secret); }; /** * Verify a JWT token * Compatible with tokens signed by jsonwebtoken library */ export const verifyToken = async (token: string): Promise => { try { const secret = getSecret(); const { payload } = await jwtVerify(token, secret); return payload; } catch (error) { if (error instanceof errors.JWTExpired) { throw new Error('Token expired'); } if (error instanceof errors.JWTInvalid) { throw new Error('Invalid token'); } throw error; } }; /** * Check if an error is a JWT-related error */ export const isJWTError = (error: unknown): boolean => { return error instanceof errors.JOSEError || (error instanceof Error && ( error.message.includes('token') || error.message.includes('JWT') )); };