diff --git a/apps/cloud/src/edge/index.ts b/apps/cloud/src/edge/index.ts index bdb166892..1b1cbe9ad 100644 --- a/apps/cloud/src/edge/index.ts +++ b/apps/cloud/src/edge/index.ts @@ -9,3 +9,4 @@ export { marketingMiddleware } from "./marketing"; export { sentryTunnelMiddleware } from "./sentry-tunnel"; export { posthogProxyMiddleware } from "./posthog"; export { docsProxyMiddleware } from "./docs"; +export { openAiAppsChallengeMiddleware } from "./openai-apps-challenge"; diff --git a/apps/cloud/src/edge/openai-apps-challenge.test.ts b/apps/cloud/src/edge/openai-apps-challenge.test.ts new file mode 100644 index 000000000..26fa95e65 --- /dev/null +++ b/apps/cloud/src/edge/openai-apps-challenge.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from "@effect/vitest"; + +import { + OPENAI_APPS_CHALLENGE_PATH, + OPENAI_APPS_CHALLENGE_TOKEN, + isOpenAiAppsChallengePath, + openAiAppsChallengeResponse, +} from "./openai-apps-challenge"; + +describe("isOpenAiAppsChallengePath", () => { + it("claims the OpenAI Apps verification challenge path", () => { + expect(isOpenAiAppsChallengePath(OPENAI_APPS_CHALLENGE_PATH)).toBe(true); + }); + + it("does not claim sibling well-known paths", () => { + expect(isOpenAiAppsChallengePath("/.well-known/openai-apps-challenge/extra")).toBe(false); + expect(isOpenAiAppsChallengePath("/.well-known/oauth-protected-resource/mcp")).toBe(false); + }); +}); + +describe("openAiAppsChallengeResponse", () => { + it("serves the exact verification token as plain text", async () => { + const response = openAiAppsChallengeResponse(); + + expect(response.status).toBe(200); + expect(response.headers.get("content-type")).toBe("text/plain; charset=utf-8"); + expect(await response.text()).toBe(OPENAI_APPS_CHALLENGE_TOKEN); + }); +}); diff --git a/apps/cloud/src/edge/openai-apps-challenge.ts b/apps/cloud/src/edge/openai-apps-challenge.ts new file mode 100644 index 000000000..a3ba1fff0 --- /dev/null +++ b/apps/cloud/src/edge/openai-apps-challenge.ts @@ -0,0 +1,23 @@ +import { createMiddleware } from "@tanstack/react-start"; + +export const OPENAI_APPS_CHALLENGE_PATH = "/.well-known/openai-apps-challenge"; +export const OPENAI_APPS_CHALLENGE_TOKEN = + "P_fW7WgF8HkXXQkP85B7aDZD_RuZv8YmQA2Zq9JoIfc"; + +export const isOpenAiAppsChallengePath = (pathname: string) => + pathname === OPENAI_APPS_CHALLENGE_PATH; + +export const openAiAppsChallengeResponse = () => + new Response(OPENAI_APPS_CHALLENGE_TOKEN, { + headers: { + "cache-control": "public, max-age=300", + "content-type": "text/plain; charset=utf-8", + }, + }); + +export const openAiAppsChallengeMiddleware = createMiddleware({ type: "request" }).server( + ({ pathname, next }) => { + if (!isOpenAiAppsChallengePath(pathname)) return next(); + return openAiAppsChallengeResponse(); + }, +); diff --git a/apps/cloud/src/start.ts b/apps/cloud/src/start.ts index d0e92dd3b..a35a327f1 100644 --- a/apps/cloud/src/start.ts +++ b/apps/cloud/src/start.ts @@ -11,6 +11,7 @@ import { prepareMcpOrgScope } from "./mcp/mount"; import { docsProxyMiddleware, marketingMiddleware, + openAiAppsChallengeMiddleware, posthogProxyMiddleware, sentryTunnelMiddleware, } from "./edge"; @@ -100,6 +101,7 @@ const appRequestMiddleware = createMiddleware({ type: "request" }).server( // load-bearing. export const startInstance = createStart(() => ({ requestMiddleware: [ + openAiAppsChallengeMiddleware, marketingMiddleware, docsProxyMiddleware, sentryTunnelMiddleware,