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:
cliffhall 2026-09-10 22:49:36 -04:00
commit a8cbc506ab

View file

@ -48,6 +48,11 @@ export interface ConfigFileOAuth {
clientName?: string;
scope?: string;
};
/**
* Where to serve `clientMetadata` (default `/client-metadata.json`);
* validated as an origin-relative path, since it becomes the document's own
* `client_id`. See `composable-test-server.ts`.
*/
clientMetadataPath?: string;
tokenExpirationSeconds?: number;
supportRefreshTokens?: boolean;
@ -260,6 +265,12 @@ function validateConfig(
`Invalid config in ${filePath}: oauth.asMetadataPath must be an origin-relative path (e.g. "/.well-known/openid-configuration") — a value such as "//host/doc" would move the document off this server entirely`,
);
}
const cimdPath = oauth.clientMetadataPath;
if (cimdPath !== undefined && !isOriginRelativePath(cimdPath)) {
throw new Error(
`Invalid config in ${filePath}: oauth.clientMetadataPath must be an origin-relative path (e.g. "/client-metadata.json") — this path becomes the document's own client_id, so a value such as "//host/doc" would publish a client id naming a host this server does not serve`,
);
}
if (transportType === "stdio" && oauth.enabled === true) {
throw new Error(
`Invalid config in ${filePath}: oauth requires streamable-http or sse transport`,