import { afterEach, expect, test } from "bun:test"; import { createContext, withContextHeaders } from "@wrnexus/core"; import type { OAuthProvider } from "@wrnexus/oauth"; import { createAuthEngine } from "../src/engine.ts"; import { createAuthHttpHandlers } from "../src/http/index.ts"; import { MemoryAuthStore } from "../src/stores/memory.ts"; const realFetch = globalThis.fetch; afterEach(() => { globalThis.fetch = realFetch; }); test("configured OAuth owns PKCE state, callback, and session establishment", async () => { const provider: OAuthProvider = { name: "example", authorizeUrl: "https://identity.example/authorize", tokenUrl: "https://identity.example/token", userInfoUrl: "https://identity.example/user", scopes: ["openid", "email"], clientId: "client", clientSecret: "secret", mapProfile: (raw) => ({ id: String(raw.id), email: String(raw.email), raw, }), }; const engine = createAuthEngine({ store: new MemoryAuthStore(), secret: "oauth-http-secret-that-is-longer-than-thirty-two-characters", }); const handlers = createAuthHttpHandlers({ engine, baseUrl: "https://app.example", oauth: { example: provider }, }); const startRequest = new Request( "https://app.example/api/auth/oauth/example?returnTo=%2Fdashboard", ); const startCtx = createContext(startRequest, new URL(startRequest.url)); startCtx.params = { provider: "example" }; const start = await handlers.startOAuth(startCtx); expect(start.status).toBe(302); const location = new URL(start.headers.get("location")!); expect(location.searchParams.get("code_challenge_method")).toBe("S256"); const issued = withContextHeaders(startCtx, start).headers.get("set-cookie")!; const replies = [ Response.json({ access_token: "access", token_type: "Bearer" }), Response.json({ id: "provider-user", email: "oauth@example.test" }), ]; const mock: typeof fetch = Object.assign(async () => replies.shift()!, { preconnect: () => undefined, }); globalThis.fetch = mock; const callbackRequest = new Request( `https://app.example/api/auth/oauth/example/callback?code=code&state=${encodeURIComponent(location.searchParams.get("state")!)}`, { headers: { cookie: issued.split(";", 1)[0]! } }, ); const callbackCtx = createContext(callbackRequest, new URL(callbackRequest.url)); callbackCtx.params = { provider: "example" }; const callback = await handlers.completeOAuth(callbackCtx); expect(callback.status).toBe(303); expect(callback.headers.get("location")).toBe("https://app.example/dashboard"); expect(callbackCtx.user).toMatchObject({ id: expect.any(String) }); });