mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-03 19:17:07 -04:00
49 lines
1.9 KiB
TypeScript
49 lines
1.9 KiB
TypeScript
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';
|
|
|
|
import { getUrlForLocale, getUrlsForAllLocales } from '~/src/domain.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(locale: string, payload: JWTPayload, expiresIn = '365d'): Promise<string> {
|
|
return await new SignJWT(payload)
|
|
.setProtectedHeader({ alg: 'RS256' })
|
|
.setExpirationTime(expiresIn)
|
|
.setAudience(getUrlsForAllLocales(locale))
|
|
.setIssuer(getUrlForLocale(locale))
|
|
.sign(this.privateKey);
|
|
}
|
|
|
|
async validate<PayloadType = JWTPayload>(locale: string, token: string): Promise<PayloadType | undefined> {
|
|
const urls = getUrlsForAllLocales(locale);
|
|
try {
|
|
const { payload } = await jwtVerify<PayloadType>(token, this.publicKey, {
|
|
algorithms: ['RS256'],
|
|
audience: urls,
|
|
issuer: urls,
|
|
});
|
|
return payload;
|
|
} catch (error) {
|
|
Sentry.captureException(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default await Jwt.from(`${rootDir}/keys/private.pem`, `${rootDir}/keys/public.pem`);
|