import { createHmac, timingSafeEqual } from "node:crypto"; import type { AppConfig } from "../config.js"; export type AdminRole = "viewer" | "operator" | "superadmin"; type TokenKind = "admin_access" | "admin_session"; type AdminTokenClaims = { aud: string; email: string; exp: number; role: AdminRole; sub: string; type: TokenKind; }; export type AdminSessionClaims = { aud: string; exp: number; operatorEmail: string; operatorId: string; role: AdminRole; }; type AdminTokenErrorCode = | "ADMIN_SESSION_REQUIRED" | "INVALID_ADMIN_ACCESS_TOKEN" | "INVALID_ADMIN_SESSION"; type AdminTokenFailure = { code: AdminTokenErrorCode; error: string; statusCode: 401; success: false; }; type AdminTokenSuccess = { success: true; value: T; }; export type AdminTokenResult = AdminTokenFailure | AdminTokenSuccess; type ExchangeResult = { expiresAt: string; operatorEmail: string; operatorId: string; role: AdminRole; sessionToken: string; }; function encodeSegment(value: unknown) { return Buffer.from(JSON.stringify(value)).toString("base64url"); } function decodeSegment(segment: string): T | null { try { return JSON.parse(Buffer.from(segment, "base64url").toString("utf8")) as T; } catch { return null; } } function signToken(claims: AdminTokenClaims, secret: string) { const header = encodeSegment({ alg: "HS256", typ: "JWT" }); const payload = encodeSegment(claims); const signature = createHmac("sha256", secret).update(`${header}.${payload}`).digest("base64url"); return `${header}.${payload}.${signature}`; } function invalidToken(error: string, code: AdminTokenErrorCode): AdminTokenFailure { return { code, error, statusCode: 401, success: false, }; } function isFailure(result: AdminTokenResult): result is AdminTokenFailure { return !result.success; } function verifyToken( token: string, config: AppConfig, expectedType: TokenKind ): AdminTokenResult { const segments = token.split("."); if (segments.length !== 3) { return invalidToken( expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session", expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION" ); } const [header, payload, signature] = segments; const expectedSignature = createHmac("sha256", config.ADMIN_TOKEN_SIGNING_SECRET) .update(`${header}.${payload}`) .digest(); const receivedSignature = Buffer.from(signature, "base64url"); if ( expectedSignature.length !== receivedSignature.length || !timingSafeEqual(expectedSignature, receivedSignature) ) { return invalidToken( expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session", expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION" ); } const claims = decodeSegment(payload); if (!claims) { return invalidToken( expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session", expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION" ); } if (claims.type !== expectedType || claims.aud !== config.ADMIN_TOKEN_AUDIENCE) { return invalidToken( expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session", expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION" ); } if (claims.exp <= Math.floor(Date.now() / 1000)) { return invalidToken( expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session", expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION" ); } return { success: true, value: claims }; } export function exchangePrivilegedToken( token: string, config: AppConfig ): AdminTokenResult { const verifiedAccessToken = verifyToken(token, config, "admin_access"); if (isFailure(verifiedAccessToken)) { return verifiedAccessToken; } const accessClaims = verifiedAccessToken.value; const sessionExpiry = Math.floor(Date.now() / 1000) + 15 * 60; const sessionToken = signToken( { aud: config.ADMIN_TOKEN_AUDIENCE, email: accessClaims.email, exp: sessionExpiry, role: accessClaims.role, sub: accessClaims.sub, type: "admin_session", }, config.ADMIN_TOKEN_SIGNING_SECRET ); return { success: true, value: { expiresAt: new Date(sessionExpiry * 1000).toISOString(), operatorEmail: accessClaims.email, operatorId: accessClaims.sub, role: accessClaims.role, sessionToken, }, }; } export function verifyAdminSession( token: string | undefined, config: AppConfig ): AdminTokenResult { if (!token) { return invalidToken("Admin session required", "ADMIN_SESSION_REQUIRED"); } const verifiedSession = verifyToken(token, config, "admin_session"); if (isFailure(verifiedSession)) { return { ...verifiedSession, code: "ADMIN_SESSION_REQUIRED", error: "Admin session required", }; } return { success: true, value: { aud: verifiedSession.value.aud, exp: verifiedSession.value.exp, operatorEmail: verifiedSession.value.email, operatorId: verifiedSession.value.sub, role: verifiedSession.value.role, }, }; }