fix(auth): carry resource_metadata into first-time legacy authorization

The challenge-driven plumbing did not reach the default CLI/TUI flow. With
no stored token in the legacy era, InspectorClient deliberately builds the
transport with no authProvider (so the SDK cannot open a browser before the
callback server is listening), which also leaves challenge interception off;
the 401 then surfaces as the SDK's headerless UnauthorizedError and the
runner calls authenticate() with no challenge in hand. So a server serving
metadata only at the advertised path still failed there — including for the
legacy showcase the README instructs.

Add a passive counterpart to the challenge interceptor: an observer fetch
that reports every 401/403 without touching control flow or the body. The
OAuthManager records the advertised URL from it and uses it in
authenticate() and its CIMD pre-registration. Latest-wins, including
clearing when a later challenge advertises none.

Also: the fixture helper dropped an explicitly supplied empty
resourceMetadataPath by truthiness, bypassing validation and silently
reverting to the well-known route.

Covered by a direct-client integration test that drives connect ->
authenticate() against the real server and asserts the advertised path was
requested and the well-known one never was; verified failing without the
fix.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-24 21:04:34 -04:00
commit 597e1bd25f

View file

@ -2914,7 +2914,10 @@ export function createOAuthTestServerConfig(options: {
mode: "combined",
requireAuth: options.requireAuth ?? false,
scopesSupported: options.scopesSupported ?? ["mcp"],
...(options.resourceMetadataPath
// `!== undefined`, not truthiness: an explicitly supplied `""` is
// invalid, and dropping it here would silently fall back to the
// well-known route instead of reporting the bad fixture (Copilot).
...(options.resourceMetadataPath !== undefined
? { resourceMetadataPath: options.resourceMetadataPath }
: {}),
staticClients: options.staticClients,