import { test, expect } from "bun:test"; import { createContext } from "@wrnexus/core"; import { createJwtKeyring, signJwt, verifyJwt, jwtAuth, JwtError } from "../src/index.ts"; const SECRET = "test-secret-key"; test("sign + verify round-trip preserves claims", async () => { const token = await signJwt({ sub: "u1", role: "admin" }, SECRET); const claims = await verifyJwt(token, SECRET); expect(claims.sub).toBe("u1"); expect(claims.role).toBe("admin"); expect(typeof claims.iat).toBe("number"); }); test("tampering or wrong secret fails verification", async () => { const token = await signJwt({ sub: "u1" }, SECRET); await expect(verifyJwt(token, "other-secret")).rejects.toThrow(JwtError); const tampered = token.slice(0, -2) + (token.endsWith("a") ? "bb" : "aa"); await expect(verifyJwt(tampered, SECRET)).rejects.toThrow(); }); test("rejects a signed token whose header declares another algorithm", async () => { const token = await signJwt({ sub: "u1" }, SECRET); const [, body] = token.split("."); const header = btoa(JSON.stringify({ alg: "none", typ: "JWT" })) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/, ""); await expect(verifyJwt(`${header}.${body}.invalid`, SECRET)).rejects.toThrow( "Unsupported token header", ); }); test("expiry is enforced", async () => { const token = await signJwt({ sub: "u1" }, SECRET, { expiresIn: 100, now: 1000 }); expect((await verifyJwt(token, SECRET, { now: 1050 })).sub).toBe("u1"); // still valid await expect(verifyJwt(token, SECRET, { now: 1200 })).rejects.toThrow("expired"); }); function ctxWith(auth?: string) { const url = new URL("http://x/api/me"); return createContext(new Request(url, { headers: auth ? { authorization: auth } : {} }), url); } test("jwtAuth sets ctx.user for a valid bearer token", async () => { const token = await signJwt({ sub: "u9" }, SECRET); const ctx = ctxWith(`Bearer ${token}`); const res = await jwtAuth({ secret: SECRET })(ctx, () => new Response("ok")); expect(res.status).toBe(200); expect((ctx.user as { sub: string }).sub).toBe("u9"); }); test("jwtAuth 401s a missing/invalid token when required", async () => { const missing = await jwtAuth({ secret: SECRET })(ctxWith(), () => new Response("ok")); expect(missing.status).toBe(401); const bad = await jwtAuth({ secret: SECRET })(ctxWith("Bearer nope"), () => new Response("ok")); expect(bad.status).toBe(401); }); test("jwtAuth optional mode passes through anonymously", async () => { const ctx = ctxWith(); const res = await jwtAuth({ secret: SECRET, required: false })(ctx, () => new Response("ok")); expect(res.status).toBe(200); expect(ctx.user).toBeUndefined(); }); test("JWT keyrings reject duplicates and return defensive copies", () => { expect(() => createJwtKeyring([ { id: "one", secret: "secret-one" }, { id: "one", secret: "secret-two" }, ]), ).toThrow("DUPLICATE"); const keyring = createJwtKeyring([{ id: "one", secret: "secret-one", active: true }]); const key = keyring.active(); key.secret = "changed"; expect(keyring.active().secret).toBe("secret-one"); });