import { expect, it } from "bun:test"; import { McpAuthDiscovery, type McpHttpFetch } from "../../src/product/mcp-auth-discovery.js"; const settings = { resource: "https://water.example/mcp", issuer: "https://identity.example/realm/water" }; const metadata = { issuer: settings.issuer, authorization_endpoint: "https://identity.example/authorize", response_types_supported: ["code"], token_endpoint: "https://identity.example/custom/token", token_endpoint_auth_methods_supported: ["client_secret_post"] }; it("uses the challenge resource metadata and discovers a non-Keycloak endpoint", async () => { const requests: string[] = []; const discovery = new McpAuthDiscovery(settings, (async (url, init) => { requests.push(String(url)); expect(new Headers(init?.headers).has("Authorization")).toBeFalse(); expect(init?.body ? String(init.body).includes("secret") : false).toBeFalse(); if (String(url) === settings.resource) return new Response(null, { status: 401, headers: { "WWW-Authenticate": 'Basic realm="gateway", Bearer resource_metadata="https://water.example/auth/metadata", scope="mcp.catalog.read"' } }); if (String(url).endsWith("/auth/metadata")) return Response.json({ resource: settings.resource, authorization_servers: [settings.issuer] }); if (String(url).includes("oauth-authorization-server")) return Response.json(metadata); throw new Error("unexpected endpoint"); }) as McpHttpFetch); expect(await discovery.resolve()).toEqual({ tokenEndpoint: metadata.token_endpoint, authMethod: "client_secret_post" }); await discovery.resolve(); expect(requests).toEqual([settings.resource, "https://water.example/auth/metadata", "https://identity.example/.well-known/oauth-authorization-server/realm/water"]); }); it("falls back through resource path, resource root and OAuth/OIDC issuer metadata", async () => { const requests: string[] = []; const discovery = new McpAuthDiscovery(settings, (async (url) => { const path = String(url); requests.push(path); if (path === settings.resource) return new Response(null, { status: 401 }); if (path === "https://water.example/.well-known/oauth-protected-resource") return Response.json({ resource: settings.resource, authorization_servers: [settings.issuer] }); if (path.endsWith("/realm/water/.well-known/openid-configuration")) return Response.json({ ...metadata, authorization_endpoint: "https://identity.example/authorize", jwks_uri: "https://identity.example/keys", response_types_supported: ["code"], subject_types_supported: ["public"], id_token_signing_alg_values_supported: ["RS256"] }); return new Response(null, { status: 404 }); }) as McpHttpFetch); expect((await discovery.resolve()).tokenEndpoint).toBe(metadata.token_endpoint); expect(requests).toEqual([ settings.resource, "https://water.example/.well-known/oauth-protected-resource/mcp", "https://water.example/.well-known/oauth-protected-resource", "https://identity.example/.well-known/oauth-authorization-server/realm/water", "https://identity.example/.well-known/openid-configuration/realm/water", "https://identity.example/realm/water/.well-known/openid-configuration", ]); }); it.each(["resource", "authority", "issuer", "endpoint", "method"])("rejects invalid %s metadata before credentials can be sent", async (kind) => { const discovery = new McpAuthDiscovery(settings, (async (url) => { if (String(url) === settings.resource) return new Response(null, { status: 401 }); if (String(url).includes("oauth-protected-resource")) return Response.json({ resource: kind === "resource" ? "https://other.example/mcp" : settings.resource, authorization_servers: [kind === "authority" ? "https://attacker.example" : settings.issuer] }); expect(String(url).startsWith("https://identity.example/")).toBeTrue(); return Response.json({ ...metadata, ...(kind === "issuer" ? { issuer: `${settings.issuer}/` } : {}), ...(kind === "endpoint" ? { token_endpoint: "http://attacker.example/token" } : {}), ...(kind === "method" ? { token_endpoint_auth_methods_supported: ["none"] } : {}) }); }) as McpHttpFetch); await expect(discovery.resolve()).rejects.toMatchObject({ status: 503 }); }); it("rechecks the configured issuer after metadata invalidation", async () => { let changed = false; const discovery = new McpAuthDiscovery(settings, (async (url) => { if (String(url) === settings.resource) return new Response(null, { status: 401 }); if (String(url).includes("oauth-protected-resource")) return Response.json({ resource: settings.resource, authorization_servers: [changed ? "https://new.example" : settings.issuer] }); return Response.json(metadata); }) as McpHttpFetch); await discovery.resolve(); changed = true; discovery.invalidate(); await expect(discovery.resolve()).rejects.toMatchObject({ status: 503 }); }); it.each(["allowed", "other-host", "other-port", "production"])("bounds development HTTP discovery: %s", async (kind) => { const issuer = "http://192.168.1.114:8088/realms/tjwater"; let requests = 0; const discovery = new McpAuthDiscovery({ ...settings, issuer, devHttpIssuer: issuer, runtimeEnvironment: kind === "production" ? "production" : "development" }, (async (url, init) => { requests++; expect(new Headers(init?.headers).has("Authorization")).toBeFalse(); expect(init?.redirect).toBe("error"); if (String(url) === settings.resource) return new Response(null, { status: 401 }); if (String(url).includes("oauth-protected-resource")) return Response.json({ resource: settings.resource, authorization_servers: [issuer] }); expect(String(url).startsWith("http://192.168.1.114:8088/")).toBeTrue(); return Response.json({ ...metadata, issuer, token_endpoint: kind === "other-host" ? "http://192.168.1.115:8088/token" : kind === "other-port" ? "http://192.168.1.114:8089/token" : `${issuer}/protocol/openid-connect/token` }); }) as McpHttpFetch); if (kind === "allowed") expect((await discovery.resolve()).tokenEndpoint).toBe(`${issuer}/protocol/openid-connect/token`); else await expect(discovery.resolve()).rejects.toMatchObject({ status: 503 }); if (kind === "production") expect(requests).toBe(0); });