Files
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

137 lines
4.9 KiB
TypeScript

import { test, expect } from "bun:test";
import {
google,
github,
discord,
defineProvider,
startAuth,
exchangeCode,
completeAuth,
randomToken,
discoverOidc,
validateOidcClaims,
} 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");
});
test("OIDC discovery enforces issuer and secure required endpoints", async () => {
const valid = await discoverOidc("https://issuer.example/", (async () =>
Response.json({
issuer: "https://issuer.example",
authorization_endpoint: "https://issuer.example/authorize",
token_endpoint: "https://issuer.example/token",
jwks_uri: "https://keys.example/jwks",
})) as unknown as typeof fetch);
expect(valid.jwks_uri).toBe("https://keys.example/jwks");
await expect(
discoverOidc("https://issuer.example", (async () =>
Response.json({
issuer: "https://attacker.example",
authorization_endpoint: "https://issuer.example/authorize",
token_endpoint: "http://issuer.example/token",
jwks_uri: "https://issuer.example/jwks",
})) as unknown as typeof fetch),
).rejects.toThrow("issuer mismatch");
});
test("OIDC claim conformance enforces nonce, subject, and authorized party", () => {
const valid = {
sub: "user-1",
iss: "https://issuer.example",
aud: ["client-1", "api"],
azp: "client-1",
exp: 200,
iat: 100,
nonce: "nonce-1",
};
expect(() => validateOidcClaims(valid, { clientId: "client-1", nonce: "nonce-1" })).not.toThrow();
expect(() =>
validateOidcClaims({ ...valid, nonce: "wrong" }, { clientId: "client-1", nonce: "nonce-1" }),
).toThrow("nonce");
expect(() => validateOidcClaims({ ...valid, azp: "other" }, { clientId: "client-1" })).toThrow(
"authorized party",
);
});