Skip to content

V2 MCP OAuth: serialize credential refresh across locations/processes #34520

Closed

Description

@rekram1-node

Context

Tracking the deferred concurrency hardening for V2 MCP OAuth (packages/core/src/mcp). The initial OAuth implementation deliberately ignores cross-location/cross-process credential refresh races; this issue captures that follow-up.

Problem

MCP OAuth tokens are stored in the global Credential table (credential.ts, makeGlobalNode), while Integration and the MCP service are location-scoped (integration.ts, makeLocationNode). The credential identity for a remote server is keyed by a hash of its (normalized) URL, so two locations using the same server URL share one global credential row.

When that shared token expires, multiple refreshers can run concurrently:

  1. Same process, multiple locations — separate location-scoped MCP/provider instances each decide to refresh the same global row. With rotating refresh tokens, the second refresh invalidates the first, dropping a location into needs_auth.
  2. Separate processes (daemon + CLI, or two servers) — no shared memory, only the shared SQLite DB. Same race, no in-memory coordination available.

Note: V1 had this race too — its file lock only serialized the write, not the decision to refresh.

Proposed direction

  • In-process: a global single-flight keyed by credential id (in the global tier alongside Credential) so concurrent refreshers coalesce onto one result.
  • Cross-process: optimistic compare-and-swap on the credential row — refresh inside a transaction, re-read, and if the stored refresh token already changed, adopt the existing fresh token instead of overwriting.

Out of scope

The base OAuth layer (provider, DCR, interactive authorize, connect-time credential read, reconnect-on-auth) ships without this; refresh works fine in the single-location happy path.

Activity

  1. self-assigned this
    on Jun 29, 2026
  2. github-actions commented on Jun 29, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • The issue does not use one of the three required templates (Bug Report, Feature Request, or Question). Please resubmit using the appropriate template — this appears to be a Bug Report or tracking issue for a known race condition.

    Please edit this issue to address the above within 2 hours, or it will be automatically closed.


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

    If you believe this was flagged incorrectly, please let a maintainer know.

  3. github-actions commented on Jun 30, 2026

    @github-actions
    Contributor

    This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.

    Feel free to open a new issue that follows our issue templates.

  4. opencode-agent commented on Jun 30, 2026

    @opencode-agent
    Contributor

    Reopening: this was auto-closed by issue compliance automation, but the author is now exempt under the team/opencode-agent automation guard.

  5. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    bugSomething isn't working
    on Jul 5, 2026
  6. neriousy commented on Oct 21, 2026

    @neriousy
    Member

    Partly done: concurrent refreshes in the same process are now shared (#48937, packages/core/src/mcp/oauth.ts), and invalidation only drops the row if it still holds our refresh token. Cross-process serialization is still open in #50994. Closing this in favor of that PR.

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

Metadata

Metadata

Assignees

Labels

2.0bugSomething isn't workingcoreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions