Commit Graph

14 Commits

Author SHA1 Message Date
can1357 3e445dccd9 test(mcp): use Promise.withResolvers in hanging-fetch helper per AGENTS.md 2026-07-23 22:15:21 +02:00
roboomp af3883c052 fix(mcp): bound oauth discovery and smithery poll fetches with abort timeouts
MCP OAuth endpoint discovery ran metadata, well-known, and recursive
authorization-server fetches with no AbortSignal, and the Smithery
browser-login poll received a never-aborting signal. An endpoint that
accepts the TCP connection but never responds stalled /mcp add,
/mcp reauth, the add wizard, or /mcp smithery-login indefinitely; the
5-minute login/poll deadlines run only after discovery resolves or
between polls, so a hung fetch never reached them.

- discoverOAuthEndpoints and fetchResourceMetadataScopes gain an optional
  signal and wrap every fetch in withTimeoutSignal(DISCOVERY_FETCH_TIMEOUT_MS,
  opts?.signal), threaded through the recursive authorization_servers call.
- pollSmitheryCliAuthSession wraps its fetch in
  withTimeoutSignal(SMITHERY_POLL_TIMEOUT_MS, signal) so a hung poll aborts
  and the loop reaches its 5-minute deadline.

Fixes #4103
2026-07-23 19:45:41 +00:00
roboomp fb98465923 fix(mcp): preserved discovered registration endpoint
Threaded the authorization server's advertised registration endpoint through OAuth discovery, add, reauth, and the client flow instead of deriving metadata from the authorization endpoint.

Added pathful-issuer discovery and end-to-end DCR regression coverage.

Fixes #5267
2026-07-14 17:46:24 +00:00
roboomp 7049966def fix(mcp): hydrate JSON-body OAuth scopes from resource metadata
When the error body already advertises OAuth endpoints, `/mcp add` and `/mcp reauth` use `authResult.oauth` directly and skip `discoverOAuthEndpoints`, so scopes advertised only in the RFC 9728 protected-resource metadata document never reach the grant.

Add exported `fetchResourceMetadataScopes(url, opts?)` that fetches the metadata doc and returns `scopes_supported` / `scopes` / `scope`. Hoist the shared `readMetadataScopes` reader out of `discoverOAuthEndpoints`. At all three call sites (wizard, `/mcp add`, `/mcp reauth`), when `oauth` is populated from the JSON body but `oauth.scopes` is empty and `authResult.resourceMetadataUrl` was advertised, fetch the metadata and merge scopes onto `oauth`.

Regression tests cover the resource-metadata fetch and its failure/empty-doc paths.

Refs #4467
2026-07-03 23:27:18 +00:00
roboomp bef1f5d773 fix(mcp): merge challenge scopes into JSON-body oauth endpoints
`/mcp reauth` and `/mcp add` consume `authResult.oauth` directly when the JSON error body carries endpoints, skipping `discoverOAuthEndpoints`. Leaving `oauth.scopes` empty there meant a challenge-only `scope="…"` still yielded a scope-less grant even though `AuthDetectionResult.scopes` recorded it.

Merge `challengeScopes` into the returned `OAuthEndpoints` inside `analyzeAuthError` so every consumer sees the same scope. Added a regression test where the JSON body advertises endpoints without `scopes` and the challenge is the only source.

Refs #4467
2026-07-03 16:15:01 +00:00
roboomp debce0757b fix(mcp): carry OAuth scopes from challenge and resource metadata
MCP servers such as JIT gateways advertise required scopes via the RFC 6750 `WWW-Authenticate` challenge (`scope="..."`) and via RFC 9728 protected-resource metadata (`scopes_supported` / `scopes` / `scope`), then reject follow-up requests with `insufficient_scope` when a bearer token was issued without them. OMP's discovery only picked up `scopes_supported` from the auth-server metadata document, so `/mcp reauth`, `/mcp add`, and the MCP add wizard silently minted scope-less tokens.

- Extract `scope`/`scopes` from the WWW-Authenticate challenge into a new `AuthDetectionResult.scopes` field via `extractOAuthChallengeScopes`.

- Thread a `protectedScopes` option through `discoverOAuthEndpoints` and its recursion; capture `scopes_supported`/`scopes`/`scope` off resource-metadata documents; use those scopes when the auth-server metadata omits them.

- Pass `authResult.scopes` from `analyzeAuthError` into every discovery call site (`/mcp reauth`, `/mcp add`, MCP add wizard).

- Add regression tests for insufficient_scope + resource_metadata, resource-metadata `scopes_supported` passthrough, and challenge-scope threading.

Fixes #4467
2026-07-03 16:10:21 +00:00
roboomp 801022bb45 fix(auth): accepted cross-host mcp oauth issuer
Allowed resource-server fallback OAuth discovery to accept authorization-server metadata whose issuer differs from the resource URL while keeping issuer matching for advertised auth-server candidates.

Added an Atlassian-shaped regression test so the fallback path no longer returns null.

Fixes #3551
2026-06-26 09:06:32 +00:00
roboomp ff6e1b6e17 fix(mcp/oauth): skip discovery metadata with mismatched issuer
`discoverOAuthEndpoints` probes `/.well-known/oauth-authorization-server` at
the origin root before path-prefixed candidates and returns on the first hit.
Plane hosts a root issuer (`https://mcp.plane.so/`) at origin root and a
separate path-scoped issuer (`https://mcp.plane.so/http`) at the path-prefixed
well-known. The `/http/mcp` endpoint advertises only the path-scoped issuer
through protected-resource metadata, so discovery should follow that issuer's
metadata — instead it accepted the wrong origin-root document and routed the
grant to `https://mcp.plane.so/authorize`, which rejects every request with
`server_error=An unexpected error occurred` before the consent screen.

RFC 8414 §3.3 requires the metadata's `issuer` to equal the URL the client
used to construct the metadata URL. Validate it in the discovery loop: when
the queried well-known is the official authorization-server or OpenID Connect
document, skip metadata whose `issuer` doesn't match (after trailing-slash
normalization). Documents without an `issuer` field keep the existing
permissive behavior so legacy/nonstandard servers continue to work.

Verified live against `https://mcp.plane.so/http/authorize` with the same
client/PKCE: pre-fix `302 -> /callback?error=server_error`, post-fix
`302 -> /http/consent?txn_id=…`. Adds an `oauth-discovery.test.ts`
regression suite covering Plane's wrong-issuer origin-root document plus
trailing-slash and no-issuer paths.

Fixes #3537
2026-06-26 07:09:24 +00:00
Clark Tomlinson df97002df9 fix: include MCP OAuth resource indicator 2026-06-10 15:58:25 -04:00
can1357 eb1a46baf5 feat: added injectable fetch transport across AI and coding network flows
- Added optional FetchImpl fields to compaction, proxy, AI, coding-agent, and mnemopi options.
- Threaded injected fetch implementations through OAuth, discovery, and search/LLM request flows.
- Removed exported hookFetch utility and its package entrypoint from utils.
- Replaced global-fetch test monkeypatching with per-test FetchImpl mocks across test suites.
2026-06-09 04:51:17 +02:00
can1357 7f27627a90 feat(mcp/oauth): added RFC 8414 path-ful issuer form to OAuth discovery
- Extended `buildWellKnownUrls` and `#resolveRegistrationEndpoint` to try `/.well-known//` as a third candidate after origin-root and path-prefixed forms.
- Fixed single-segment path handling so `/my-service` is treated as the gateway prefix rather than dropped.
- Fixed missing `await` on `#tryWellKnownForRegistration` that caused path-prefixed fallback to return an unresolved Promise.
- Added tests for single-segment prefix discovery and RFC 8414 path-ful issuer fallback.
2026-05-26 20:22:25 +02:00
Mohd Faiz Hasim 5be0f01a91 feat(coding-agent): support OAuth discovery for path-prefixed auth servers behind gateways
- Extract resource_metadata URL from WWW-Authenticate and follow RFC 9728 chain
- Add buildWellKnownUrls with path-prefixed well-known fallback for gateways
- Fix resolveRegistrationEndpoint to try path-prefixed well-known (was missing await)
- Support relative Mcp-Auth-Server URL resolution against server URL
- Pass resourceMetadataUrl through all discoverOAuthEndpoints call sites
- Add comprehensive tests for path-prefixed, resource_metadata, and relative URL flows
2026-05-26 22:50:32 +08:00
can1357 005401e82a refactor: extracted fetch mocking into reusable hookFetch utility
- Extracted fetch mocking logic into reusable `hookFetch()` utility function with middleware-style handler pattern.
- Replaced manual `globalThis.fetch` assignment and restoration across 10 test files with `hookFetch()` calls using `using` statement for automatic cleanup.
- Implemented Disposable pattern with Symbol.dispose for fetch hook resource management, eliminating try-finally blocks.
- Exported `hookFetch` from utils public API to enable consistent fetch mocking across packages.
2026-03-08 16:16:54 +01:00
can1357 2551dc04a2 feat(mcp): added MCP OAuth server metadata discovery with header parsing
- Added `authServerUrl` field to `AuthDetectionResult` to capture MCP OAuth server metadata.
- Added `extractMcpAuthServerUrl()` function to parse and validate `Mcp-Auth-Server` header URLs from OAuth errors.
- Enhanced `discoverOAuthEndpoints()` to accept optional `authServerUrl` parameter and query `/.well-known/oauth-protected-resource` endpoint.
- Improved OAuth metadata extraction to handle multiple `clientId` field variations (`clientId`, `default_client_id`, `public_client_id`).
- Extracted metadata parsing logic into reusable `findEndpoints()` helper function supporting multiple OAuth metadata formats.
- Added comprehensive test coverage for OAuth endpoint discovery, header parsing, and error validation.

Fixes #235
2026-03-01 15:26:52 +01:00