import type { PathLike } from 'node:fs'; import fs from 'node:fs/promises'; import * as Sentry from '@sentry/node'; import { importPKCS8, importSPKI, jwtVerify, SignJWT } from 'jose'; import type { KeyLike, JWTPayload } from 'jose'; import { rootDir } from './paths.ts'; class Jwt { constructor(private privateKey: KeyLike, private publicKey: KeyLike) {} static async from(privateKeyPath: PathLike, publicKeyPath: PathLike) { const privateKeyPromise = fs.readFile(privateKeyPath, 'utf-8') .then((privateKeyContent) => importPKCS8(privateKeyContent, 'RS256')); const publicKeyPromise = fs.readFile(publicKeyPath, 'utf-8') .then((publicKeyContent) => importSPKI(publicKeyContent, 'RS256')); const [privateKey, publicKey] = await Promise.all([privateKeyPromise, publicKeyPromise]); return new Jwt(privateKey, publicKey); } async sign(payload: JWTPayload, expiresIn = '365d'): Promise { return await new SignJWT(payload) .setProtectedHeader({ alg: 'RS256' }) .setExpirationTime(expiresIn) .setAudience(JSON.parse(process.env.NUXT_PUBLIC_ALL_LOCALES_URLS ?? '[]')) .setIssuer(process.env.NUXT_PUBLIC_BASE_URL!) .sign(this.privateKey); } async validate(token: string): Promise { try { const { payload } = await jwtVerify(token, this.publicKey, { algorithms: ['RS256'], audience: JSON.parse(process.env.NUXT_PUBLIC_ALL_LOCALES_URLS ?? '[]'), issuer: JSON.parse(process.env.NUXT_PUBLIC_ALL_LOCALES_URLS ?? '[]'), }); return payload; } catch (error) { Sentry.captureException(error); } } } export default await Jwt.from(`${rootDir}/keys/private.pem`, `${rootDir}/keys/public.pem`);