68 lines
1.8 KiB
TypeScript
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)
|
|
}
|