Move project and host queries into native MCP with host-owned OAuth/context, while retaining memory, skills, actions and publication plugins. Add nearby-model queries and explicit chat/task/learning model defaults. Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped session ownership, pending input edits and investigation communication. Disable local-provider discovery: Server /health allowed native vLLM to probe /v1/models on port 8000. A native before/after regression prevents recurrence. Validation: 170 focused Bun tests; service/plugin typechecks; native discovery, provider-discovery and input-inbox smoke checks; no paid model inference.
79 lines
6.1 KiB
TypeScript
79 lines
6.1 KiB
TypeScript
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);
|
|
});
|