Skip to content

[rmcp-client] Refresh OAuth credentials after invalid_token responses - #26522

Closed
anp-oai wants to merge 3 commits into
mainfrom
codex/oauth-reactive-401-repro
Closed

[rmcp-client] Refresh OAuth credentials after invalid_token responses#26522
anp-oai wants to merge 3 commits into
mainfrom
codex/oauth-reactive-401-repro

Conversation

@anp-oai

@anp-oai anp-oai commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Intent

Recover only when an OAuth-backed MCP server rejects a Bearer access token with invalid_token before its locally recorded expiry, without introducing general retries or replaying side-effecting tool calls.

User-visible behavior

Before

An MCP server can revoke or otherwise reject an OAuth access token while Codex still considers it unexpired. When the server responds with a Bearer invalid_token challenge, initialization or a tool call fails without refreshing the credentials. The user may need to manually reconnect or log in again before the MCP server works.

After

  • If the initial initialize request receives Bearer invalid_token, Codex force-refreshes the OAuth credentials, rebuilds the transport, and retries initialization once within the original timeout. A second rejection is returned without another retry.
  • If tools/call receives Bearer invalid_token, Codex refreshes the OAuth credentials but does not replay the potentially side-effecting tool call. The failed call returns a clear retry-required error, and the user's next call uses the refreshed token and can succeed.
  • A rejected or missing refresh token returns an actionable reauthentication-required error.
  • Static bearer tokens, environment/custom authorization headers, and generic 401 responses keep their existing AuthRequired behavior and do not enter the OAuth refresh path.

Concrete implementation

  • Gate reactive recovery on the presence of an OAuthPersistor and classify only Bearer invalid_token challenges.
  • During the initialize request, force-refresh credentials, rebuild the OAuth transport, and retry exactly once within the original startup timeout.
  • For tools/call, perform at most one forced refresh for the failed operation, never replay the original call, and return a stable retry-required error.
  • Surface rejected or missing refresh tokens through the reauthentication signal shared with [rmcp-client] Classify OAuth refresh and storage failures correctly #26519.
  • Assert rotated refresh-token persistence while leaving broader token-rotation behavior to [rmcp-client] Preserve refresh tokens omitted by refresh responses #26520.
  • Add deterministic exact-count coverage for generic 401 exclusion, the second-401 initialize retry cap, no tool replay, caller-issued retry success, and static-bearer semantics.

Validation not covered by CI

  • just test -p codex-rmcp-client --test streamable_http_oauth_startup (9 passed)
  • just test -p codex-rmcp-client --test streamable_http_recovery (7 passed)
  • just test -p codex-rmcp-client --lib (53 passed)
  • just fix -p codex-rmcp-client
  • just fmt

Deferred

@anp-oai anp-oai changed the title [rmcp-client] Reproduce premature OAuth 401 behavior [rmcp-client] Refresh OAuth credentials after invalid_token responses Jun 5, 2026
@anp-oai

anp-oai commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

handing this off for now

@anp-oai anp-oai closed this Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant