import jwt from 'jsonwebtoken' export type ActorType = 'admin' | 'employee' | 'renter' export type ActorTokenPayload = jwt.JwtPayload & { sub: string type: ActorType sessionVersion?: number last2faAt?: number } const ISSUER = 'rentaldrivego-api' const ALG: jwt.Algorithm[] = ['HS256'] function getJwtSecret() { const secret = process.env.JWT_SECRET if (!secret) throw new Error('JWT_SECRET is required') return secret } export function signActorToken( actorId: string, actorType: ActorType, options: Pick & { sessionVersion?: number; last2faAt?: number } = {}, ) { const { sessionVersion, last2faAt, ...signOptions } = options return jwt.sign( { sub: actorId, type: actorType, ...(sessionVersion === undefined ? {} : { sessionVersion }), ...(last2faAt === undefined ? {} : { last2faAt }), }, getJwtSecret(), { algorithm: 'HS256', issuer: ISSUER, audience: actorType, expiresIn: signOptions.expiresIn ?? '8h', }, ) } export function verifyActorToken(token: string, expectedType: ActorType): ActorTokenPayload { const payload = jwt.verify(token, getJwtSecret(), { algorithms: ALG, issuer: ISSUER, audience: expectedType, }) as jwt.JwtPayload if (typeof payload.sub !== 'string' || payload.type !== expectedType) { throw new Error('Invalid actor token') } return payload as ActorTokenPayload } export function verifyAnyActorToken(token: string): ActorTokenPayload { const decoded = jwt.decode(token) as jwt.JwtPayload | null const actorType = decoded?.type if (actorType !== 'admin' && actorType !== 'employee' && actorType !== 'renter') { throw new Error('Invalid actor token') } return verifyActorToken(token, actorType) }