MCP OAuth reauth succeeds but active session still uses stale refresh token (invalid_grant)

Open 💬 10 comments Opened Mar 9, 2026 by vinzenz
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

After re-authenticating an OAuth-enabled MCP server, the currently running Codex agent/session can continue using a stale refresh token and still fail MCP initialization with invalid_grant until the app/session is restarted.

Expected behavior

After successful codex mcp login <server>, MCP handshakes in the active session should use fresh credentials immediately (or the runtime should force re-init of auth state).

Observed behavior

  • codex mcp login <server> reports success.
  • MCP tool calls in the same running session continue failing with:

OAuth token refresh failed: invalid_grant: Invalid or expired refresh token

  • Restarting the app/new session clears the issue.

Steps to reproduce

  1. Configure any OAuth-enabled MCP server.
  2. Let its refresh token become invalid/expired.
  3. In a running Codex session, trigger MCP usage and observe invalid_grant failure.
  4. Run codex mcp login <server> and complete OAuth successfully.
  5. Retry MCP usage in the same running session.
  6. Observe continued invalid_grant until restart/new session.

Environment

  • Codex CLI: codex-cli 0.107.0
  • Platform: macOS 26.3.1 (25D2128)
  • Date observed: 2026-03-10

Notes

This appears to be runtime credential cache invalidation rather than OAuth login failure. A post-login cache refresh or MCP client reinitialization would likely resolve it.

View original on GitHub ↗

10 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #13956
  • #13852

Powered by Codex Action

strobil · 2 months ago

Adding another data point for a private Sourcegraph Cloud MCP server.

Environment:

  • Codex CLI: codex-cli 0.128.0
  • MCP server: streamable_http, OAuth
  • URL: https://<redacted>.sourcegraphcloud.com/.api/mcp

Observed pattern:

  • Right after restarting Codex, the MCP server works (list_repos, keyword_search).
  • After Codex has been running for a while, the MCP session can get stuck until re-auth/restart.

Captured failure from the same MCP config:

resources/list failed: failed to get client: MCP startup failed: handshaking with MCP server failed: Send message error Transport [...] error: Auth error: OAuth token refresh failed: Server returned error response: invalid_grant: The provided authorization grant [...] or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The refresh token is malformed or not valid., when send initialize request

I also checked the current rust-v0.128.0 source. My read is:

This looks consistent with this issue and #17265: preemptive refresh exists, but invalid_grant / 401 does not force MCP client reinit, clear stale auth state, or prompt re-login for the active session.

dsghi · 1 month ago

Adding another current data point, now with Codex CLI 0.133.0.

I’m seeing the same active-session stale refresh-token behavior against an OAuth/DCR MCP server. Exiting and restarting Codex CLI restores access, suggesting that startup performs a full reconnection, but the live session does not recover after token expiry/refresh failure.

I may be oversimplifying, but from the outside it seems like an MCP auth failure during an active session should trigger a reconnect/reinitialize path for that specific MCP server, similar to what happens when the CLI is restarted. FWIW, the constant failures are burning tokens, and this is also a pain in the ass hahaha.

Server-side trace, same MCP client ID [bracketed notes added]:

2026-05-26T18:48:42Z  [CLI started] refresh OK: 56c8bea9022e -> b27e4188070f
2026-05-26T19:00:31Z  [CLI attempt with stale token] refresh rejected: reason=missing refreshTokenFingerprint=56c8bea9022e
2026-05-26T19:00:32Z  [CLI attempt with stale token] AppendDocument rejected: access JWT lifetime validation failed
2026-05-26T19:00:40Z  [CLI attempt with stale token] refresh rejected again with same 56c8bea9022e
2026-05-26T19:00:49Z  [CLI attempt with stale token] refresh rejected again with same 56c8bea9022e
2026-05-26T19:05:31Z  [CLI restart] refresh OK: b27e4188070f -> 826f8aa06b83
jcarioti · 1 month ago

I may be oversimplifying, but from the outside it seems like an MCP auth failure during an active session should trigger a reconnect/reinitialize path for that specific MCP server, similar to what happens when the CLI is restarted.

Are you oversimplifying or is this just how every OAuth client has worked for the entire history of OAuth? 😅

I'm genuinely baffled that this bug has been present for so long... no one at OpenAI uses an OAuth-backed MCP with a long-running client session? Like.. really?

I'm having similar issues, FWIW.

Marsssssssssssdsss · 1 month ago

Hi @vinzenz, the stale refresh token persisting after re-authentication is a subtle state management issue — the session holds a reference to the old token pair and doesn't invalidate it when the new pair is created.

The fix is conceptually straightforward but requires careful implementation: each OAuth session should have a unique session ID, and when re-authentication completes with a new token pair, the old session ID should be invalidated so any in-flight requests using the old refresh token fail fast instead of hanging.

The fact that it requires an app restart to clear the stale state suggests the session ID is tied to the process lifetime rather than the auth flow. Have you confirmed whether this is a client-side or server-side issue?

gabrielbryk · 1 month ago

I am running into this issue constantly, I can't imagine other's aren't as MCP OAuth is a VERY common use case. How is this not a priority issue?

mamonaki · 1 month ago

Adding a Windows Codex Desktop data point that appears to combine the stale-auth recovery issue here with the tool-import symptom in #20009.

Environment

  • Codex Desktop runtime: 0.137.0-alpha.4
  • Platform: Windows
  • MCP transport: remote streamable_http, OAuth
  • MCP server: Woodstock (https://mcp.app.woodstock.co/mcp)

Observed sequence

  1. This MCP previously worked and exposed tools in Codex.
  2. Its access token expired. The server issues an access token with expires_in: 86399 (~24 hours) and does not issue a refresh token.
  3. Codex stopped exposing the MCP tools, but did not surface an auth-expired/reauth-required prompt.
  4. Restarting Codex Desktop and removing/re-adding the MCP config did not recover it. Removing/re-adding the server also did not clear the old Windows Credential Manager entry (<server>|<id>.Codex MCP Credentials).
  5. After manually deleting that exact stale credential from Windows Credential Manager, reconnecting triggered OAuth and saved a newly issued credential.
  6. The new access token was verified as currently valid. Using the exact newly stored bearer token, a direct MCP initialize POST to the configured server returned HTTP 200, Content-Type: text/event-stream, and a valid MCP initialize result.
  7. Despite that, the active Codex Desktop runtime still never imports the server tools. New independent threads also see zero tools.

Repeated Codex log state

waiting for MCP server tools while building tool list
server_name=woodstock
has_cached_tool_info_snapshot=false
startup_complete=true

There is never a corresponding listed MCP server tools ... server_name=woodstock event. Built-in codex_apps does return its tool list normally.

Additional auth behavior

  • The expired-token endpoint returns HTTP 401, but in this case the response did not include a WWW-Authenticate header.
  • The newly issued credential contains access_token, token_type, expires_in, and scope, but no refresh_token.
  • This means silent refresh is impossible for this provider, but Codex should still recover by clearly requesting reauthentication and rebuilding the MCP client/tool registry after successful reauth.

Expected behavior

When a remote MCP access token expires and no refresh token exists, Codex should:

  1. surface a reauthentication-required state instead of hanging while listing tools;
  2. clear/replace stale persisted credentials when the server is removed or reauthenticated;
  3. recreate the MCP connection and import tools immediately after successful reauth.

The direct authenticated initialize succeeding while Codex remains stuck strongly suggests stale runtime auth/connection state rather than a currently invalid token or unavailable MCP server.

alexeysavitski · 7 days ago

Additional reproduction from Codex Desktop on macOS (app-server 0.144.2), 2026-07-13. The MCP endpoint is a private OAuth-backed Streamable HTTP server; no static bearer-token header is configured.

Evidence:

  • The active task had a stored Keychain credential with a non-empty refresh token. Its access token expired at 17:42:35 MSK (expires_in=900).
  • At 18:04:50 MSK, a fresh isolated codex exec process updated the same MCP credential record without running codex mcp login or a browser flow. The new stored access token had expires_in=28800 and expiry 02:04:50 MSK the next day. This confirms that automatic refresh and persistence can succeed for this server.
  • Immediately after that update, the original Desktop task still failed on a read-only MCP call with:
tool call failed for `logs-fleet/list_services`
Transport send error: ... StreamableHttpClientAdapter ... Auth required

The active task's error path contains no OAuth AuthClient wrapper, which is consistent with a previously initialized static bearer/fallback transport. It did not re-read the updated Keychain credential or recreate the transport after the successful refresh.

Expected behavior:

  • After a refresh succeeds in another Codex process, or after codex mcp login <server> completes, active MCP clients should invalidate/reinitialize their transport and retry a read-only operation once.
  • A 401 from an existing bearer/fallback transport should trigger OAuth rediscovery/reinitialization (or a precise reauth-required error), not leave the task permanently returning generic Auth required.

Context: OAuth metadata discovery had been temporarily unavailable earlier in this task and was later restored. That likely explains the initial fallback selection, but the demonstrated defect is that the already-running task does not recover after valid credentials are available.

smalljimmy · 7 days ago

I reproduced this against current main and prepared a targeted fix here:

https://github.com/smalljimmy/codex/tree/codex/mcp-auth-hot-reload

Root cause: RmcpClient already retains a transport recipe that rereads the pinned OAuth credential store when rebuilding, but live-operation recovery only rebuilt for session-expired 404s. AuthRequired, AuthorizationRequired, and TokenExpired transport failures therefore left the running service attached to its old in-memory OAuth manager even after another process (for example, codex mcp login) replaced the persisted credentials.

The patch reuses the existing serialized, per-client transport recovery path for those authentication-required failures and retries the failed operation exactly once. It does not poll the credential store, restart unrelated MCP servers, or change the existing behavior for a plain 401 without a WWW-Authenticate challenge.

Regression coverage creates a live OAuth MCP client with an initial token, replaces the persisted credential as a separate login process would, makes the initial tools/call fail with a Bearer challenge, and verifies the same client rebuilds and retries with the replacement token. The test failed with Auth required before the production change and passes afterward.

Verification:

  • just test -p codex-rmcp-client: 111 passed, 6 skipped
  • just fix -p codex-rmcp-client
  • just fmt

Commit: https://github.com/smalljimmy/codex/commit/006f9bd6c7

The contribution guide says external PRs require an explicit maintainer invitation. If this approach matches the intended solution, please invite the PR and I will open it immediately.

pwiner · 3 days ago

+1. Please start the integration process on this. It's a critical flaw that prevents using Codex MCP.