import { SignJWT, jwtVerify, decodeJwt } from "jose"; import type { JWTPayload } from "jose"; const rawSecret = process.env.JWT_SECRET; if (!rawSecret) { if (process.env.NODE_ENV === "production") { throw new Error("JWT_SECRET environment variable is required in production"); } rawSecret = "dev-secret-change-in-production"; } const JWT_SECRET = new TextEncoder().encode(rawSecret); const ACCESS_TOKEN_EXPIRY = "15m"; export interface AccessTokenPayload extends JWTPayload { userId: number; sessionId: string; role: string; } export async function signAccessToken(payload: { userId: number; sessionId: string; role: string; }): Promise { return new SignJWT(payload as Record) .setProtectedHeader({ alg: "HS256" }) .setIssuedAt() .setExpirationTime(ACCESS_TOKEN_EXPIRY) .sign(JWT_SECRET); } export async function verifyAccessToken( token: string ): Promise { try { const { payload } = await jwtVerify(token, JWT_SECRET); return payload as AccessTokenPayload; } catch { return null; } } export function decodeAccessTokenNoVerify(token: string): AccessTokenPayload | null { try { return decodeJwt(token) as AccessTokenPayload; } catch { return null; } }