freshyo/apps/backend/src/lib/jwt-utils.ts
2026-03-22 16:38:37 +05:30

72 lines
1.7 KiB
TypeScript

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<string, unknown>,
expiresIn: string = '7d'
): Promise<string> => {
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<JWTPayload> => {
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')
));
};