import { base64 } from "./deps.ts";
const header = base64.encode(JSON.stringify({ alg: "HS256" }));const encoder = new TextEncoder();const decoder = new TextDecoder();const random = new Uint8Array(32);crypto.getRandomValues(random);const rand = decoder.decode(random);const fallback = base64.encode(rand);const signingAlg = { name: "HMAC", hash: "SHA-256" } as const;
const keyCache = new Map<string, CryptoKey>();
async function importKey(key: string): Promise<CryptoKey> { let k = keyCache.get(key); if (k) { return k; }
k = await crypto.subtle.importKey( "raw", encoder.encode(key), signingAlg, false, ["sign", "verify"], ); keyCache.set(key, k); return k;}
async function sign(data: string, key: string): Promise<string> { const k = await importKey(key); return base64.encode( await crypto.subtle.sign( signingAlg, k, encoder.encode(data), ), );}
async function verify( data: string, sig: string, keys: string[],): Promise<boolean> { for (const key of keys) { const k = await importKey(key); if ( await crypto.subtle.verify( signingAlg, k, base64.decode(sig), encoder.encode(data), ) ) { return true; } } return false;}
export async function encodeJwt( payload: unknown, keys = fallback as string | string[],): Promise<string> { const key = Array.isArray(keys) ? keys[0] : keys; const jwt = `${header}.${base64.encode(JSON.stringify(payload))}`; const sig = await sign(jwt, key); return `${jwt}.${sig}`;}
export async function decodeJwt( jwt: string, keys = fallback as string | string[],): Promise<unknown> { const parts = jwt.split(".");
if (parts.length !== 3) { throw new Error("Invalid JWT - bad format"); }
try { const header = decoder.decode(base64.decode(parts[0])); const h = JSON.parse(header); if (h.alg !== "HS256") { throw null; } } catch { throw new Error("Invalid JWT - unsupported header"); }
keys = Array.isArray(keys) ? keys : [keys]; if (!await verify(`${parts[0]}.${parts[1]}`, parts[2], keys)) { throw new Error("Invalid JWT - bad signature"); }
try { const payload = decoder.decode(base64.decode(parts[1])); return JSON.parse(payload); } catch { throw new Error("Invalid JWT - bad payload"); }}