Files
buzz-sheet/lib/auth/oidc-flow.test.ts
T
gunshiz 4b16941e11
CI / Verify (push) Successful in 1m31s
CI / Build immutable images and deploy (push) Successful in 2m16s
fix(auth) : keep Guide sessions beyond Sudloh token expiry
2026-10-06 18:00:42 +07:00

67 lines
3.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { betterAuth } from "better-auth";
import { memoryAdapter } from "better-auth/adapters/memory";
import { genericOAuth } from "better-auth/plugins";
const origin = "https://guide.test";
function createReplica(database: Record<string, Record<string, unknown>[]>) {
return betterAuth({
baseURL: origin,
secret: "a-shared-test-secret-with-enough-entropy-123",
database: memoryAdapter(database),
rateLimit: { enabled: false },
plugins: [genericOAuth({ config: [{
providerId: "sudloh",
clientId: "test-client",
clientSecret: "test-secret",
authorizationUrl: "https://account.test/authorize",
tokenUrl: "https://account.test/token",
getToken: async () => ({ accessToken: "test-access-token", accessTokenExpiresAt: new Date(Date.now() + 3600_000) }),
getUserInfo: async () => ({ id: "test-subject", email: "[email protected]",
emailVerified: true, name: "Test User" }),
}] })],
});
}
describe("Sudloh OAuth callback", () => {
it("completes across replicas, creates the Guide session, and consumes state once", async () => {
const database = { user: [], session: [], account: [], verification: [] };
const first = createReplica(database);
const second = createReplica(database);
const start = await first.handler(new Request(`${origin}/api/auth/sign-in/social`, {
method: "POST", headers: { Origin: origin, "Content-Type": "application/json" },
body: JSON.stringify({ provider: "sudloh", callbackURL: "/profile" }),
}));
expect(start.status).toBe(200);
const authorization = new URL((await start.json()).url);
expect(authorization.searchParams.get("code_challenge_method")).toBe("S256");
expect(authorization.searchParams.get("code_challenge")).toBeTruthy();
const stateCookie = start.headers.get("set-cookie")?.split(";", 1)[0];
expect(stateCookie).toBeTruthy();
const callbackURL = new URL(`${origin}/api/auth/callback/sudloh`);
callbackURL.searchParams.set("code", "test-code");
callbackURL.searchParams.set("state", authorization.searchParams.get("state")!);
const callback = await second.handler(new Request(callbackURL, {
headers: { Cookie: stateCookie! },
}));
expect(callback.status).toBe(302);
expect(callback.headers.get("location")).toBe("/profile");
const sessionCookie = callback.headers.getSetCookie()
.find((cookie) => cookie.includes("session_token="))?.split(";", 1)[0];
const session = await second.api.getSession({ headers: new Headers({ Cookie: sessionCookie! }) });
expect(session?.user.email).toBe("[email protected]");
const replay = await first.handler(new Request(callbackURL, {
headers: { Cookie: stateCookie! },
}));
expect(replay.headers.get("location")).toContain("state_mismatch");
});
it("rejects a callback without state before exchanging a code", async () => {
const auth = createReplica({ user: [], session: [], account: [], verification: [] });
const response = await auth.handler(new Request(`${origin}/api/auth/callback/sudloh?code=test-code`));
expect(response.status).toBe(302);
expect(response.headers.get("location")).toContain("state_not_found");
});
});