Files
WRNexusJS/packages/oauth/test/oauth.test.ts
T
2026-07-12 15:55:18 +05:30

96 lines
3.4 KiB
TypeScript

import { test, expect } from "bun:test";
import {
google,
github,
discord,
defineProvider,
startAuth,
exchangeCode,
completeAuth,
randomToken,
} from "../src/index.ts";
const CREDS = { clientId: "cid", clientSecret: "secret" };
test("presets have the right endpoints + scopes", () => {
expect(google(CREDS).authorizeUrl).toContain("accounts.google.com");
expect(github(CREDS).scopes).toContain("user:email");
expect(discord(CREDS).userInfoUrl).toContain("discord.com/api/users/@me");
});
test("startAuth builds an authorize URL with PKCE + state", async () => {
const { url, state, verifier } = await startAuth(google(CREDS), {
redirectUri: "https://app.test/cb",
});
const u = new URL(url);
expect(u.origin + u.pathname).toBe("https://accounts.google.com/o/oauth2/v2/auth");
expect(u.searchParams.get("client_id")).toBe("cid");
expect(u.searchParams.get("redirect_uri")).toBe("https://app.test/cb");
expect(u.searchParams.get("response_type")).toBe("code");
expect(u.searchParams.get("scope")).toBe("openid email profile");
expect(u.searchParams.get("state")).toBe(state);
expect(u.searchParams.get("code_challenge")).toBeTruthy();
expect(u.searchParams.get("code_challenge_method")).toBe("S256");
expect(u.searchParams.get("access_type")).toBe("offline"); // provider default param
expect(verifier.length).toBeGreaterThan(20);
});
test("randomToken is URL-safe and unique", () => {
const a = randomToken();
const b = randomToken();
expect(a).not.toBe(b);
expect(a).toMatch(/^[A-Za-z0-9_-]+$/);
});
test("exchangeCode posts the code + PKCE verifier and parses tokens", async () => {
let captured: { url: string; body: string } | null = null;
const fakeFetch = (async (url: string, init: RequestInit) => {
captured = { url: String(url), body: String(init.body) };
return new Response(JSON.stringify({ access_token: "tok", token_type: "Bearer" }), {
headers: { "content-type": "application/json" },
});
}) as unknown as typeof fetch;
const tokens = await exchangeCode(github(CREDS), {
code: "abc",
redirectUri: "https://app.test/cb",
verifier: "ver123",
fetch: fakeFetch,
});
expect(tokens.access_token).toBe("tok");
expect(captured!.url).toBe("https://github.com/login/oauth/access_token");
expect(captured!.body).toContain("code=abc");
expect(captured!.body).toContain("code_verifier=ver123");
expect(captured!.body).toContain("grant_type=authorization_code");
});
test("completeAuth maps the provider profile (custom provider)", async () => {
const provider = defineProvider({
name: "acme",
authorizeUrl: "https://acme.test/authorize",
tokenUrl: "https://acme.test/token",
userInfoUrl: "https://acme.test/me",
scopes: ["email"],
clientId: "cid",
clientSecret: "secret",
mapProfile: (raw) => ({ id: String(raw.user_id), email: raw.mail as string, name: "n", raw }),
});
const fakeFetch = (async (url: string) => {
if (String(url).endsWith("/token"))
return new Response(JSON.stringify({ access_token: "t" }), {
headers: { "content-type": "application/json" },
});
return new Response(JSON.stringify({ user_id: 99, mail: "x@acme.test" }), {
headers: { "content-type": "application/json" },
});
}) as unknown as typeof fetch;
const { profile } = await completeAuth(provider, {
code: "c",
redirectUri: "r",
fetch: fakeFetch,
});
expect(profile.id).toBe("99");
expect(profile.email).toBe("x@acme.test");
});