Add direct-keyring regression coverage for concurrent MCP OAuth refreshes

Open 💬 0 comments Opened Jul 16, 2026 by runminglu

What version of Codex CLI is running?

0.144.4 when observed; 0.145.0-alpha.16 now installed (contains #32229)

What subscription do you have?

Pro; not relevant to MCP OAuth credential persistence

Which model were you using?

N/A — MCP startup and OAuth credential refresh

What platform is your computer?

Darwin 25.4.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

Terminal.app and ChatGPT desktop app (concurrent Codex processes)

Codex doctor report

Not included: the current installed build already contains #32229; this report documents an observed 0.144.4 race and proposes missing direct-keyring regression coverage.

What issue are you seeing?

Two concurrent Codex processes shared an OAuth MCP credential through macOS Keychain. On Codex 0.144.4, the standalone client successfully refreshed a rotating token, but the Keychain entry remained on the predecessor. A later app-server process loaded that stale entry and the provider rejected its refresh with invalid_grant, making the MCP server appear to lose authorization.

Observed sequence:

  • The standalone process refreshed the credential successfully.
  • No successful Keychain save followed that refresh, and the durable entry remained stale.
  • A second Codex process later loaded the stale Keychain credential.
  • Its refresh attempted to reuse the rotated predecessor and was rejected.

#32229 appears to address the production root cause by serializing each credential's read-refresh-write transaction, rereading the authoritative store after locking, persisting before exposure, and letting later processes adopt the winner. Its successful concurrency regression currently exercises the JSON file store; the equivalent successful direct-keyring path is not covered end to end.

What steps can reproduce the bug?

  1. Configure a remote OAuth MCP server that rotates refresh tokens and use the direct keyring credential backend.
  2. Start two Codex processes that share the same CODEX_HOME and macOS Keychain entry (for example, standalone CLI plus app-server).
  3. Let the stored access token expire so both clients need a proactive refresh.
  4. Allow the first process to refresh, then let the second process refresh from the shared durable credential.
  5. Without serialized durable persistence, the second process can replay the rotated predecessor and receive invalid_grant.

The proposed deterministic regression forces both clients through the real per-credential lock contention path, expects exactly one provider refresh, and verifies the second client adopts the direct-keyring winner.

What is the expected behavior?

The first process should persist the refreshed credential to the lifecycle-pinned Keychain store before exposing it. The second process should acquire the credential lock, reread Keychain, observe the unexpired winner, and adopt it without contacting the provider. No fallback file should be created.

Additional information

I prepared a focused test-only commit and would like an invitation to submit it under the repository's external-contribution policy.

I searched the existing MCP OAuth/keyring issues and did not find one covering this exact successful direct-keyring concurrency regression gap.

View original on GitHub ↗