Public: anyone can read this; the crew can change it.

GET /.well-known/oauth-protected-resource answers RFC 9728 JSON naming this forge's /mcp and its authorization server #2397

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/2391-get-well-known-oauth-protected-resource → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/2391-get-well-known-oauth-protected-resource was merged into main as 10a93a2.

Review

Sign in to review this change.
1 file changed +39 −0
test/oauth-protected-resource.test.ts added +39
@@ -0,0 +1,39 @@
1 /**
2 * RFC 9728: `/.well-known/oauth-protected-resource` names this forge's /mcp
3 * and its authorization server, from the configured URL and never a header.
4 */
5 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
6 import { mkdtemp, rm } from "node:fs/promises";
7 import { tmpdir } from "node:os";
8 import { join } from "node:path";
9 import { createApp, type App } from "../src/app.ts";
10 import { serve, type Servers } from "../src/http/server.ts";
11
12 describe("oauth protected resource metadata", () => {
13 let dataDir: string;
14 let app: App;
15 let servers: Servers;
16
17 beforeAll(async () => {
18 dataDir = await mkdtemp(join(tmpdir(), "forge-oprm-"));
19 app = createApp({ dataDir, port: 0, url: "https://forge.example" });
20 servers = await serve(app);
21 });
22 afterAll(async () => {
23 servers.stop();
24 app.close();
25 await rm(dataDir, { recursive: true, force: true });
26 });
27
28 test("names the configured origin's /mcp, even when headers claim another", async () => {
29 const res = await fetch(`${servers.url}/.well-known/oauth-protected-resource`, {
30 headers: { "x-forwarded-host": "evil.test", "x-forwarded-proto": "http" },

Good: the spoofed forwarded headers are what make this test worth having. It is the regression guard for publicUrl ignoring client-written headers.

31 });
32 expect(res.status).toBe(200);
33 expect(res.headers.get("content-type")).toContain("application/json");
34 const doc = (await res.json()) as Record<string, unknown>;
35 expect(doc.resource).toBe("https://forge.example/mcp");
36 expect(doc.authorization_servers).toEqual(["https://forge.example"]);
37 expect(doc.bearer_methods_supported).toEqual(["header"]);
38 });
39 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs