Skip to content

fix(opencode): secure manual MCP OAuth callback#33725

Open
rekram1-node wants to merge 2 commits into
devfrom
secure-oauth-api
Open

fix(opencode): secure manual MCP OAuth callback#33725
rekram1-node wants to merge 2 commits into
devfrom
secure-oauth-api

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • keep split/manual MCP OAuth start listener-free while preserving browser authentication callback behavior
  • require callback state and atomically validate/consume it before exchanging the authorization code
  • reject mismatched and replayed state without allowing a wrong-state request to cancel the legitimate flow
  • centrally clear pending transport, state, and PKCE verifier on terminal outcomes
  • regenerate the JavaScript SDK callback payload and documentation

Security rationale

The split API previously accepted an authorization code without binding it to the state returned by mcp.auth.start, allowing an uncorrelated callback and replay. It also started a local browser callback listener that the manual API never consumed and retained pending OAuth material after failures. This change binds each callback to one atomically consumed state value, rejects invalid/replayed callbacks before token exchange, preserves the expected state after a mismatched request, avoids exposing the unused listener in manual mode, and closes/clears pending credentials at completion.

Related issues

Tests

  • bun test test/server/httpapi-mcp.test.ts test/server/httpapi-mcp-oauth.test.ts test/mcp/oauth-browser.test.ts test/mcp/oauth-auto-connect.test.ts test/mcp/auth.test.ts --timeout 30000 (16 passed)
  • bun typecheck from packages/opencode
  • ./packages/sdk/js/script/build.ts
  • Prettier check for all modified source and test files

The HTTP integration tests use production Effect routes and a real local OAuth/MCP HTTP server without mocks or sleeps. They cover missing, wrong, correct, and replayed state, prove wrong state does not exchange a token or invalidate the expected state, and verify manual start leaves the configured callback port unbound.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant