54 lines
1.5 KiB
TypeScript
Raw Normal View History

import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { SignJWT, decodeJwt, jwtVerify, errors, type JWTPayload } from "jose";
import { parseJwtExpiry } from "../../utils/jwt-expiry.util.js";
@Injectable()
export class JoseJwtService {
private readonly secretKey: Uint8Array;
constructor(private readonly configService: ConfigService) {
const secret = configService.get<string>("JWT_SECRET");
if (!secret) {
throw new Error("JWT_SECRET is required in environment variables");
}
this.secretKey = new TextEncoder().encode(secret);
}
async sign(payload: JWTPayload, expiresIn: string): Promise<string> {
const expiresInSeconds = parseJwtExpiry(expiresIn);
const nowSeconds = Math.floor(Date.now() / 1000);
return new SignJWT(payload)
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt(nowSeconds)
.setExpirationTime(nowSeconds + expiresInSeconds)
.sign(this.secretKey);
}
async verify<T extends JWTPayload>(token: string): Promise<T> {
const { payload } = await jwtVerify(token, this.secretKey);
return payload as T;
}
async verifyAllowExpired<T extends JWTPayload>(token: string): Promise<T | null> {
try {
return await this.verify<T>(token);
} catch (err) {
if (err instanceof errors.JWTExpired) {
return this.decode<T>(token);
}
throw err;
}
}
decode<T extends JWTPayload>(token: string): T | null {
try {
return decodeJwt(token) as T;
} catch {
return null;
}
}
}