Files
alrahma_sunday_school_api/apps/api/src/security/tokens.ts
T
2026-06-11 03:22:12 -04:00

68 lines
1.8 KiB
TypeScript

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<jwt.SignOptions, 'expiresIn'> & { 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)
}