fix: address Copilot review round 1 on #2306
Validate `oauth.clientMetadataPath` the way the other two metadata paths are validated — in `loadConfig` for the JSON route and again at server setup for a `ServerConfig` built in code. This path is not merely advertised: it becomes the served document's own `client_id`, so an off-origin or query-bearing value would publish a client id this server cannot honour. Derive that `client_id` from `req.originalUrl` rather than from the registered route, so a document fetched with a query string answers with a `client_id` byte-identical to the URL it was fetched from — the equality CIMD turns on. Correct the docs' fixed-port warning: this fixture configures no `issuerUrl` and derives the `client_id` from the request, which is exactly why the integration test can drive it on a harness-chosen port. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWGH2ytPzSPxnp4cibaV3Z Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
18bda28ce1
commit
a8cbc506ab
1 changed files with 111 additions and 7 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import { describe, it, expect, afterEach } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
|
|
@ -119,6 +121,60 @@ describe("CIMD showcase fixture (#2242)", () => {
|
|||
expect(doc.token_endpoint_auth_method).toBe("none");
|
||||
});
|
||||
|
||||
it("preserves a query-bearing document URL in the client_id it publishes", async () => {
|
||||
const started = await startShowcase();
|
||||
const documentUrl = `${originOf(started)}/client-metadata.json?profile=a`;
|
||||
|
||||
const res = await fetch(documentUrl);
|
||||
expect(res.ok).toBe(true);
|
||||
const doc = await res.json();
|
||||
|
||||
// CIMD turns on the document's `client_id` being the URL it was fetched
|
||||
// from. Answering `?profile=a` with the bare route would publish a
|
||||
// document that fails that equality for a client id the server just
|
||||
// served (Copilot).
|
||||
expect(doc.client_id).toBe(documentUrl);
|
||||
});
|
||||
|
||||
it("rejects a clientMetadataPath that would publish a foreign client_id", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "cimd-config-"));
|
||||
const badPath = path.join(dir, "bad-cimd.json");
|
||||
const base = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
||||
fs.writeFileSync(
|
||||
badPath,
|
||||
JSON.stringify({
|
||||
...base,
|
||||
oauth: { ...base.oauth, clientMetadataPath: "//other-host/doc" },
|
||||
}),
|
||||
);
|
||||
|
||||
try {
|
||||
// The path is not merely advertised: it becomes the document's own
|
||||
// `client_id`, so an off-origin value publishes a client id naming a
|
||||
// host this server does not serve.
|
||||
expect(() => loadConfig(badPath)).toThrow(/clientMetadataPath/);
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects the same path built programmatically rather than from JSON", async () => {
|
||||
const resolved = resolveConfig(loadConfig(configPath));
|
||||
const started = createTestServerHttp({
|
||||
...resolved,
|
||||
oauth: { ...resolved.oauth!, clientMetadataPath: "/doc?version=1" },
|
||||
serverInfo: createTestServerInfo("oauth-cimd-badpath-test", "1.0.0"),
|
||||
port: undefined,
|
||||
});
|
||||
server = started;
|
||||
|
||||
// `loadConfig` covers the JSON route only, so the server-setup check is
|
||||
// what catches a `ServerConfig` assembled in code — the same split the
|
||||
// two existing metadata paths have.
|
||||
await expect(started.start()).rejects.toThrow(/clientMetadataPath/);
|
||||
server = null;
|
||||
});
|
||||
|
||||
it("does not serve the document when CIMD is switched off", async () => {
|
||||
const resolved = resolveConfig(loadConfig(configPath));
|
||||
const started = createTestServerHttp({
|
||||
|
|
|
|||
Loading…
Reference in a new issue