Serialize shared MCP OAuth credential stores#30292
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c85609830
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
c99bdc7 to
e4dcca6
Compare
e4dcca6 to
f1e1c17
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f1e1c1711a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
|
||
| use codex_keyring_store::tests::MockKeyringStore; | ||
|
|
||
| #[path = "store_lock_tests.rs"] |
There was a problem hiding this comment.
This should be done on the store_lock module itself, rather than transitively through this test module.
There was a problem hiding this comment.
[from Codex] Addressed in 9b7f3b3. The store-lock tests are now included from the store_lock module itself instead of transitively through oauth.rs.
| [target.'cfg(any(target_os = "freebsd", target_os = "openbsd"))'.dependencies] | ||
| keyring = { workspace = true, features = ["sync-secret-service"] } | ||
|
|
||
| # This test is compiled through `#[path]` inside the inline `oauth::tests` module. Cargo-shear |
There was a problem hiding this comment.
This should be removeable if you have the test module included in the right place (see other comment).
There was a problem hiding this comment.
[from Codex] Addressed in 9b7f3b3. Moving the test module under store_lock made the cargo-shear ignored-paths exception unnecessary, so I removed it.
| match file.try_lock() { | ||
| Ok(()) => return Ok(Self { _file: file }), | ||
| Err(std::fs::TryLockError::WouldBlock) if started.elapsed() >= acquire_timeout => { | ||
| anyhow::bail!( |
There was a problem hiding this comment.
Can you use thiserror to define an error enum for this module instead of stringifying the failure mode?
There was a problem hiding this comment.
[from Codex] Addressed in 9b7f3b3. OAuthStoreLockFailure is now a thiserror enum with explicit CODEX_HOME, create-dir, open, timeout, and lock variants while preserving the existing anyhow downcast behavior used to prevent Auto fallback on coordination failures.
| if !reported_contention { | ||
| tracing::debug!( | ||
| target: LOCK_CONTENTION_EVENT_TARGET, | ||
| store = store.description(), |
There was a problem hiding this comment.
nit: should description just be an impl std::fmt::Display for OauthStore?
There was a problem hiding this comment.
[from Codex] Addressed in 9b7f3b3. OAuthStore now implements Display and the lock code uses that instead of a separate description() helper.
|
|
||
| impl OAuthStoreLock { | ||
| pub(super) fn acquire(store: OAuthStore) -> Result<Self> { | ||
| let codex_home = find_codex_home().context(OAuthStoreLockFailure { |
There was a problem hiding this comment.
Sorry for not raising this earlier (easier to see it's an issue with a smaller more focused change), but how is this going to work in situations where there's no CODEX_HOME? We're generally working to make that possible for other embedders (can share details on slack) so we need to think through how this lock will work in that environment.
There was a problem hiding this comment.
From Codex
“no CODEX_HOME env var” is already supported by defaulting to ~/.codex. “No home / no local filesystem authority at all” is not supported by the current File/Secrets credential stores.
For #30292, the new lock is consistent with the existing local store contract: it puts the lock under the same resolved codex home, e.g. ~/.codex/mcp-oauth-locks/.... That means this PR is not introducing the first dependency on codex-home-like local state for File/Secrets mode; the credential stores already have that dependency.
There was a problem hiding this comment.
[from Codex] Added a code comment in 9b7f3b3 documenting the boundary. This lock intentionally follows the existing local File/Secrets credential-store authority: when CODEX_HOME is unset, those stores already resolve to the default home (~/.codex); when an embedder has no local home/filesystem authority at all, those stores already cannot operate as-is. A future provider-backed credential store should provide its own matching lock authority rather than using this local lock path.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b7f3b3abb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .unwrap_or_else(PoisonError::into_inner); | ||
| let dir = tempdir().expect("create CODEX_HOME temp dir"); | ||
| unsafe { | ||
| std::env::set_var("CODEX_HOME", dir.path()); |
There was a problem hiding this comment.
Stop mutating CODEX_HOME in this test helper
Because this helper mutates the process-global CODEX_HOME, it only serializes the new store_lock_tests with this module-local lock; under single-process runners such as Bazel/libtest, the existing oauth.rs tests in the same binary use a different TempCodexHome guard and can run concurrently, so either module can read/write the other temp credential store or remove CODEX_HOME mid-test. Share one serialization guard or pass the home path into the helpers instead.
AGENTS.md reference: AGENTS.md:L210-L214
Useful? React with 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d5eb78ec3d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,181 @@ | |||
| //! Cross-process serialization for MCP OAuth stores shared by multiple credentials. | |||
There was a problem hiding this comment.
Split the oversized OAuth locking change
This patch is 830 changed lines (git show --numstat: 775 insertions, 55 deletions) and the OAuth locking work is not mechanical, so it crosses the repo's 800-line review-size limit. Please split into a smaller coherent first stage, such as introducing the lock primitive plus File-store serialization and its tests, then follow with Secrets locking and Auto fallback behavior.
AGENTS.md reference: AGENTS.md:L127-L131
Useful? React with 👍 / 👎.
Codex Thread 019edd6d-6f14-74e2-853c-345d1803d4a6
Stack
Review and merge in order. Every layer is independently correct and documents its safe stopping point.
This PR is layer 1.
Why
MCP OAuth credentials stored in File or Secrets share one aggregate map. Concurrent read-modify-write operations for different MCP servers can both read the same snapshot and let the later write discard the earlier update. That is a correctness problem independent of refresh-token rotation.
What this PR does
WouldBlock, rather than assuming a sleeping worker reached the lock.Decisions and non-goals
CODEX_HOME, matching the aggregate files it protects.Safe stopping point
This PR can merge alone. It prevents lost updates and partial aggregate reads. Auto can still resolve again during a client lifecycle until layer 2, and concurrent refreshes remain possible until layer 3.
Validation
just test -p codex-rmcp-client(96 passed; expected environment skips)