Repository navigation
Conversation
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
The following comment was made by an LLM, it may be inaccurate: |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
BrickerP
left a comment
There was a problem hiding this comment.
I read this against the v1 head (3a48c08) and the 1.32.0 SDK sources. The core change looks right to me:
McpOAuthPendingProvider.tokens()only returns staged tokens, so once the server advertises authorization-server metadata, the standaloneauth()call instartAuthalways ends inREDIRECT. That means an explicit login really does go through the browser instead of being satisfied by a publicinitialize, which is exactly what #55138 asks for. Themcp authCLI already asks for confirmation before re-authenticating, so this doesn't add surprise prompts.- The
callToolproxy covers the only tool-invocation path (McpCatalog.convertTool→client.callTool). Thes.clients[name] === clientandstatus === "connected"guards keep a stale client from flipping a server that has already been replaced, and the rejection is rethrown rather than replayed. - Passing
fetchFnfromcreateFetchWithInit(undefined, headers)matches what the transport hands toauth()itself, andauth.jsstill wraps discovery, token and registration fetches infetchWithinOrigin. So I don't see a new cross-origin header exposure here.
One regression I think is worth handling before merge: discoverOAuthServerInfo now runs unconditionally, before connect. It swallows protected-resource-metadata errors, but discoverAuthorizationServerMetadata throws on any 5xx (HTTP 5xx trying to load OAuth metadata from …, around client/auth.js:713-719 in 1.32.0). Before this PR, discovery only happened after a 401. So take a remote server that doesn't use OAuth at all (public, or static-header auth with oauth left at its default) and sits behind a gateway or catch-all route that answers unknown /.well-known/* paths with 500/502/503. opencode mcp auth <name> (or the in-app authenticate) used to connect. Now it hits Effect.die with a metadata error. The new tests only stub 404 for well-known paths, so they don't catch this.
A small fix that keeps the intended behavior is to treat a failed discovery the same as "no metadata" and fall through to the existing connect path. If the server really does need OAuth, the transport's own 401 handling still captures the redirect, with the WWW-Authenticate hints:
const discovery = await discoverOAuthServerInfo(url, { fetchFn }).catch(() => undefined)
if (
discovery?.authorizationServerMetadata &&
(await auth(authProvider, { serverUrl: url, scope: oauthConfig?.scope, fetchFn })) !== "AUTHORIZED"
) {
throw new UnauthorizedError()
}A regression test with the stub returning 503 for /.well-known/oauth-authorization-server and /.well-known/openid-configuration, expecting connected, would pin it down.
Non-blocking: auth() repeats the PRM and AS discovery that discoverOAuthServerInfo just did, so each explicit login makes those requests twice. If you want to avoid that, passing resourceMetadataUrl from discovery.resourceMetadata (or just letting auth() do the discovery and checking its result) would remove the duplicate round trips.
|
Fixed in 18527bd. Optional metadata discovery now falls back to the existing connect path when discovery fails. The explicit OAuth call still runs when metadata is present; the transport and SDK redirect guards are unchanged. Both new regressions failed on the previous head: OAuth metadata 503 and OpenID metadata 503, with the latter using a static Authorization header. Both now connect without opening authorization. All 72 MCP tests and the package type check pass. Not doing: discovery deduplication in this backport. It is non-blocking and would broaden this fix. |
BrickerP
left a comment
There was a problem hiding this comment.
Re-checked 18527bd against my earlier note. The .catch(() => undefined) on discoverOAuthServerInfo plus the discovery?. guard resolves it: a 5xx from the AS or OpenID well-known now falls through to the normal connect path instead of failing mcp auth, and an OAuth-enabled server with working metadata still goes through the explicit auth() call. The two new 503 regressions (OAuth metadata, and OpenID metadata with a static Authorization header) cover exactly the gateway case, and asserting authorizeCalls() === 0 is a good guard against an accidental browser open.
One small behavioral note, not blocking: the catch also swallows transient network errors on an OAuth-protected server, so mcp auth would connect, get a 401, and land in needs_auth rather than surfacing the discovery error. That seems like the right trade for this backport.
Looks good to me.
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
Issue for this PR
Closes #55138 (#55138)
Type of change
What does this PR do?
Fix native OAuth login when an MCP server permits anonymous initialization and tool discovery. Explicit login now invokes SDK OAuth rather than treating a successful connection as authenticated. A protected tool authentication failure changes the current server to
needs_auth; the failed operation is not replayed.Anonymous discovery and background GET 401 remain available without a login prompt.
oauth: falsekeeps its existing behavior. Upgrade SDK 1.29.0 to 1.32.0 and rebase the existing patch to retain the upstream cross-origin redirect security fix.Targets the maintained
v1branch, which contains the affected v1.19.0 implementation;devhas a different MCP implementation.How did you verify your code works?
41 focused tests and the package typecheck pass. The new regressions fail on the old behavior. Verified native source CLI browser consent, PKCE/token exchange and authenticated reconnect against a localhost server using fake credentials. No model calls or production accounts used. The three reviewed patches are unchanged after rebasing onto current
v1.Screenshots / recordings
Authentication/state logic only; existing status and toast UI reused.
Checklist