Skip to content
File

Blob: tests/helpers/oidc-mock.ts

typescript111 lines
1import { OIDCMockProvider, type OIDCMockProviderConfig } from "@mongodb-js/oidc-mock-provider";
2import type { IncomingMessage, ServerResponse } from "node:http";
3 
4export const TEST_OIDC_CLIENT_ID = "anvil-e2e";
5export const TEST_OIDC_CLIENT_SECRET = "anvil-e2e-tessera-secret";
6 
7export const E2E_BASELINE_TESSERA_SUB = "e2e-baseline-sub";
8export const E2E_BASELINE_EMAIL = "e2e-operator@example.com";
9export const E2E_BASELINE_NAME = "E2E Operator";
10export const E2E_BASELINE_SLUG = "e2e-operator";
11 
12export interface MockIdentity {
13 sub: string;
14 email: string;
15 email_verified?: boolean;
16 name?: string;
17}
18 
19export interface MockOidcServer {
20 issuer: string;
21 close(): Promise<void>;
22}
23 
24const BASELINE_IDENTITY: MockIdentity = {
25 sub: E2E_BASELINE_TESSERA_SUB,
26 email: E2E_BASELINE_EMAIL,
27 email_verified: true,
28 name: E2E_BASELINE_NAME,
29};
30 
31let nextIdentity: MockIdentity | null = null;
32 
33const selectIdentity = (): MockIdentity => {
34 if (!nextIdentity) {
35 return BASELINE_IDENTITY;
36 }
37 
38 const identity = nextIdentity;
39 nextIdentity = null;
40 return identity;
41};
42 
43const handleControlRoute = (url: string, req: IncomingMessage, res: ServerResponse): void => {
44 if (!url.includes("/__test/")) return;
45 
46 const parsed = new URL(url);
47 if (parsed.pathname !== "/__test/identity") return;
48 
49 if (req.method !== "GET") {
50 res.statusCode = 405;
51 res.end(JSON.stringify({ error: "method_not_allowed" }));
52 return;
53 }
54 
55 const sub = parsed.searchParams.get("sub");
56 const email = parsed.searchParams.get("email");
57 const name = parsed.searchParams.get("name") ?? undefined;
58 const emailVerified = parsed.searchParams.get("email_verified") !== "false";
59 
60 if (!sub || !email) {
61 res.statusCode = 400;
62 res.end(JSON.stringify({ error: "invalid_identity" }));
63 return;
64 }
65 
66 nextIdentity = { sub, email, email_verified: emailVerified, name };
67 res.statusCode = 200;
68 res.end(JSON.stringify({ ok: true }));
69};
70 
71export const startMockOidcProvider = async (): Promise<MockOidcServer> => {
72 const config: OIDCMockProviderConfig = {
73 hostname: "127.0.0.1",
74 getTokenPayload: () => {
75 const identity = selectIdentity();
76 return {
77 expires_in: 3600,
78 payload: { sub: identity.sub, scope: "openid email profile" },
79 customIdTokenPayload: {
80 sub: identity.sub,
81 email: identity.email,
82 email_verified: identity.email_verified !== false,
83 name: identity.name,
84 },
85 };
86 },
87 overrideRequestHandler: handleControlRoute,
88 };
89 const provider = await OIDCMockProvider.create(config);
90 return {
91 issuer: provider.issuer,
92 close: () => provider.close(),
93 };
94};
95 
96export const setMockOidcIdentity = async (issuer: string, identity: MockIdentity): Promise<void> => {
97 const params = new URLSearchParams({
98 sub: identity.sub,
99 email: identity.email,
100 email_verified: identity.email_verified === false ? "false" : "true",
101 });
102 if (identity.name) {
103 params.set("name", identity.name);
104 }
105 
106 const response = await fetch(`${issuer}/__test/identity?${params.toString()}`);
107 if (!response.ok) {
108 throw new Error(`Failed to configure mock OIDC identity: ${response.status} ${await response.text()}`);
109 }
110};