1import { describe, expect, it } from "vite-plus/test";
2import { EnvironmentId, ProviderInstanceId } from "@t3tools/contracts";
3import {
4 codexAuthorizationRequest,
5 codexAuthDeliveryUrl,
6 codexAuthHandoffUrl,
7 readCodexAuthDelivery,
8 readCodexAuthHandoff,
9} from "./codexAuthHandoff.ts";
10
11const authorizationUrl = () => {
12 const url = new URL("https://auth.openai.com/api/accounts/authorize");
13 url.search = new URLSearchParams({
14 client_id: "dynamic_agent_client",
15 response_type: "code",
16 redirect_uri: "http://127.0.0.1:54213/auth/callback",
17 state: "a".repeat(43),
18 code_challenge_method: "S256",
19 code_challenge: "b".repeat(43),
20 }).toString();
21 return url.toString();
22};
23const input = {
24 authorizationUrl: authorizationUrl(),
25 returnUrl: "https://app.t3.codes/welcome#agents:remote-environment",
26 environmentId: EnvironmentId.make("remote-environment"),
27 instanceId: ProviderInstanceId.make("work-codex"),
28 flowId: "flow-one",
29};
30const callbackUrl = `http://127.0.0.1:54213/auth/callback?state=${"a".repeat(43)}&code=one-time-code&client_id=oaiapp_test`;
31
32describe("Codex desktop handoff", () => {
33 it("keeps the hosted return route, account, and environment with the code in a fragment", () => {
34 expect(readCodexAuthHandoff(codexAuthHandoffUrl(input), false)).toEqual(input);
35 const delivery = codexAuthDeliveryUrl(input, callbackUrl);
36 expect(new URL(delivery).search).toBe("");
37 expect(readCodexAuthDelivery(delivery)).toEqual({
38 callbackUrl,
39 environmentId: input.environmentId,
40 instanceId: input.instanceId,
41 flowId: input.flowId,
42 returnHash: "#agents:remote-environment",
43 returnUrl: input.returnUrl,
44 });
45 });
46 it("rejects other handlers, schemes, arbitrary return sites, and non-OpenAI authorization", () => {
47 const link = codexAuthHandoffUrl(input);
48 expect(readCodexAuthHandoff(link, true)).toBeUndefined();
49 expect(readCodexAuthHandoff(link.replace("auth/codex", "auth/other"), false)).toBeUndefined();
50 expect(
51 readCodexAuthHandoff(
52 codexAuthHandoffUrl({ ...input, returnUrl: "https://attacker.example/welcome" }),
53 false,
54 ),
55 ).toBeUndefined();
56 expect(
57 readCodexAuthHandoff(
58 codexAuthHandoffUrl({
59 ...input,
60 authorizationUrl: input.authorizationUrl.replace("auth.openai.com", "attacker.example"),
61 }),
62 false,
63 ),
64 ).toBeUndefined();
65 });
66 it("rejects duplicated authorization parameters and non-loopback callback addresses", () => {
67 expect(() =>
68 codexAuthorizationRequest(
69 input.authorizationUrl + "&redirect_uri=http://localhost:1/auth/callback",
70 ),
71 ).toThrow();
72 const url = new URL(input.authorizationUrl);
73 url.searchParams.set("redirect_uri", "http://localhost:54213/auth/callback");
74 expect(() => codexAuthorizationRequest(url.toString())).toThrow();
75 url.searchParams.set("redirect_uri", "https://attacker.example/auth/callback");
76 expect(() => codexAuthorizationRequest(url.toString())).toThrow();
77 expect(() => codexAuthDeliveryUrl(input, callbackUrl + "&state=another")).toThrow();
78 });
79});