Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/modules/admin-sso/state.server.test.ts
T

105 lines
4.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ADMIN_SSO_CHALLENGE_COOKIE,
ADMIN_SSO_CHALLENGE_TTL_MS,
consumeAdminSsoChallenge,
createAdminSsoChallenge,
serializeAdminSsoChallengeCookie,
} from "./state.server";
const SECRET = "test-secret-do-not-use-in-prod";
function cookieValue(cookie: string): string {
const head = cookie.split(";")[0] ?? "";
return head.slice(`${ADMIN_SSO_CHALLENGE_COOKIE}=`.length);
}
function requestWithCookie(value: string): Request {
return new Request("https://admin.example.test/auth/admin/callback", {
headers: { cookie: `${ADMIN_SSO_CHALLENGE_COOKIE}=${value}` },
});
}
describe("createAdminSsoChallenge", () => {
it("produces distinct state, nonce and codeVerifier for each invocation", () => {
const a = createAdminSsoChallenge({ secret: SECRET });
const b = createAdminSsoChallenge({ secret: SECRET });
expect(a.payload.state).not.toBe(b.payload.state);
expect(a.payload.nonce).not.toBe(b.payload.nonce);
expect(a.payload.codeVerifier).not.toBe(b.payload.codeVerifier);
// state should be 32 random bytes hex-encoded -> 64 chars
expect(a.payload.state).toMatch(/^[0-9a-f]{64}$/);
expect(a.payload.nonce).toMatch(/^[0-9a-f]{64}$/);
});
it("derives codeChallenge from codeVerifier (PKCE S256)", () => {
const { payload } = createAdminSsoChallenge({ secret: SECRET });
expect(payload.codeChallenge).toMatch(/^[A-Za-z0-9_-]+$/);
expect(payload.codeChallenge).not.toBe(payload.codeVerifier);
});
it("emits an HttpOnly, Path-scoped cookie", () => {
const { cookie } = createAdminSsoChallenge({ secret: SECRET });
expect(cookie).toMatch(/HttpOnly/);
expect(cookie).toMatch(/Path=\/auth\/admin/);
expect(cookie).toMatch(/SameSite=Lax/);
});
});
describe("consumeAdminSsoChallenge", () => {
it("round-trips a fresh challenge and returns a clear-cookie header", () => {
const { payload, cookie } = createAdminSsoChallenge({ secret: SECRET, returnTo: "/admin/dashboard" });
const value = cookieValue(cookie);
const req = requestWithCookie(value);
const result = consumeAdminSsoChallenge(req, { secret: SECRET });
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.payload.state).toBe(payload.state);
expect(result.payload.nonce).toBe(payload.nonce);
expect(result.payload.codeVerifier).toBe(payload.codeVerifier);
expect(result.payload.returnTo).toBe("/admin/dashboard");
expect(result.clearCookie).toContain(`${ADMIN_SSO_CHALLENGE_COOKIE}=`);
expect(result.clearCookie).toContain("Max-Age=0");
}
});
it("reports `missing` when no cookie is attached", () => {
const req = new Request("https://admin.example.test/auth/admin/callback");
const result = consumeAdminSsoChallenge(req, { secret: SECRET });
expect(result.ok).toBe(false);
if (!result.ok) expect(result.reason).toBe("missing");
});
it("rejects a cookie with a tampered signature", () => {
const { cookie } = createAdminSsoChallenge({ secret: SECRET });
const value = cookieValue(cookie);
const dot = value.lastIndexOf(".");
const tampered = value.slice(0, dot) + ".AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
const result = consumeAdminSsoChallenge(requestWithCookie(tampered), { secret: SECRET });
expect(result.ok).toBe(false);
if (!result.ok) expect(result.reason).toBe("bad-signature");
});
it("rejects a cookie signed under a different secret", () => {
const { cookie } = createAdminSsoChallenge({ secret: SECRET });
const result = consumeAdminSsoChallenge(requestWithCookie(cookieValue(cookie)), { secret: "different-secret" });
expect(result.ok).toBe(false);
if (!result.ok) expect(result.reason).toBe("bad-signature");
});
it("rejects an expired cookie (older than TTL)", () => {
const past = Date.now() - ADMIN_SSO_CHALLENGE_TTL_MS - 1000;
const { payload } = createAdminSsoChallenge({ secret: SECRET, now: () => past });
const cookie = serializeAdminSsoChallengeCookie(payload, SECRET);
const result = consumeAdminSsoChallenge(requestWithCookie(cookieValue(cookie)), { secret: SECRET });
expect(result.ok).toBe(false);
if (!result.ok) expect(result.reason).toBe("expired");
});
it("rejects a malformed cookie value", () => {
const result = consumeAdminSsoChallenge(requestWithCookie("garbage"), { secret: SECRET });
expect(result.ok).toBe(false);
if (!result.ok) expect(["malformed", "bad-signature"]).toContain(result.reason);
});
});