Skip to content

fix(mcp): silent fallback when OAuth callback port is in use surfaces as misleading CSRF error #30888

Closed as not planned

Description

@WUKUNTAI-0211

Summary

When 127.0.0.1:19876 (the hardcoded OAuth callback port) is already in use, McpOAuthCallback.ensureRunning() silently returns without starting our callback server:

https://github.com/sst/opencode/blob/dev/packages/opencode/src/mcp/oauth-callback.ts#L153-L157

The OAuth flow then opens the browser anyway and waits for a callback. The browser hits the other process listening on 19876, which either 404s or — if it's another opencode instance — sees a state it doesn't know about and shows:

Authorization Failed
Invalid or expired state parameter - potential CSRF attack

That message points the user at a CSRF / cookie / browser issue. The real cause is "the callback port is occupied".

How I hit it

Shared dev host with two users. The other user's long-running bun dev serve had grabbed 19876 days ago. My opencode mcp auth Notion failed every time with the CSRF error. Took an hour to trace.

Why it matters

  • The fields to fix it (oauth.callbackPort, oauth.redirectUri) already exist on McpOAuthConfig and are wired through ensureRunning. The user just has no way to know they need to set them.
  • Anyone on a shared host, dev container, GitHub Codespaces, or a machine where 19876 happens to be taken (it's not in IANA's well-known range but it's not reserved either) gets the same misleading error.

Proposed fix

Throw a clear error from ensureRunning when the port is busy, naming the config knob:

OAuth callback port 19876 is already in use. Set "oauth.callbackPort"
(or "oauth.redirectUri") on the MCP server entry in your opencode config
to use a different port.

This changes one branch from "silent return" to "throw", so it's a small behaviour change. The only thing it breaks is the (apparently undocumented) ability for two opencode instances on the same machine to share one callback server — which is racy anyway (states are kept in the first instance's memory, so the second's auth would silently fail).

Happy to PR — will post one shortly.

Activity

  1. github-actions commented on Jun 5, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    The core difference is your report focuses on the case where the occupying process is not another opencode instance (e.g. a different user's process), while those issues focus on multiple opencode instances. Both cases stem from the same silent-return behavior in ensureRunning() and the same misleading CSRF error. It may be worth commenting on one of those existing issues rather than opening a new one, or at least linking them.

  2. github-actions commented on Aug 5, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  3. mattv8 commented on Aug 11, 2026

    @mattv8

    This is still an issue. I'm using Openchamber. All OAuth attempts use 127.0.0.1:19876. If another OpenCode/OpenChamber process owns that port, the new process silently continues but stores its OAuth state in a separate in-memory map. The callback then reaches the wrong process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions