Keyring failure silently falls back to plaintext file storage, leaving stale credentials on disk
What version of Codex CLI is running?
main (latest commit 4b31848f5)
What subscription do you have?
API User
Which model were you using?
_N/A — this is a credential storage issue, not model-specific_
What platform is your computer?
Darwin 25.1.0 arm64 (also reproducible on Linux)
What terminal emulator and version are you using (if applicable)?
_N/A_
What issue are you seeing?
When the system keyring is unavailable (headless servers, containers, or keyring service crashes), AutoAuthStorage silently falls back to loading credentials from a plaintext JSON file (~/.codex/auth.json) without any user-visible warning.
Additionally, when credentials are successfully saved to the keyring, the code attempts to delete the fallback file but silently swallows the error if deletion fails:
// codex-rs/core/src/auth/storage.rs lines 201-209
fn save(&self, auth: &AuthDotJson) -> std::io::Result<()> {
let key = compute_store_key(&self.codex_home)?;
let serialized = serde_json::to_string(auth).map_err(std::io::Error::other)?;
self.save_to_keyring(&key, &serialized)?;
if let Err(err) = delete_file_if_exists(&self.codex_home) {
warn!("failed to remove CLI auth fallback file: {err}");
}
Ok(())
}
The fallback loading path (storage.rs lines 241-250) also logs only a warn! on keyring failure before falling back:
fn load(&self) -> std::io::Result<Option<AuthDotJson>> {
match self.keyring_storage.load() {
Ok(Some(auth)) => Ok(Some(auth)),
Ok(None) => self.file_storage.load(),
Err(err) => {
warn!("failed to load CLI auth from keyring, falling back to file storage: {err}");
self.file_storage.load()
}
}
}
There's also a file permission concern in storage.rs lines 118-124 — OpenOptions::mode(0o600) only applies when the file is created, not when an existing file is opened with truncate. If the file already exists with wider permissions (e.g. 0o644), the credentials get written to a world-readable file.
What steps can reproduce the bug?
- Log in to Codex CLI normally (credentials saved to keyring + fallback file)
- Stop the keyring service (e.g.,
systemctl --user stop gnome-keyringon Linux, or just run in a container/SSH session without keyring) - Start Codex CLI — it loads credentials from the plaintext file silently, only a
warn!log is emitted - Even after keyring is restored and credentials are re-saved to keyring, the plaintext file may remain on disk if
delete_file_if_existsfails
What is the expected behavior?
- When falling back to file storage, the user should see a clear visible warning (not just a log entry) explaining that credentials are being loaded from disk
- If the fallback file cannot be deleted after keyring save, the error should be surfaced to the user rather than silently swallowed
- File permissions should be explicitly set (via
fchmodor equivalent) on every write, not just on file creation - Ideally, the CLI should refuse to use file-based credentials in security-sensitive operations, or at least require explicit user opt-in for file storage
Additional information
The combination of silent fallback + stale credentials on disk creates an unnecessary attack surface. An attacker with local file write access could replace the credentials file while the keyring is unavailable, and the CLI would load the tampered credentials without question. This is a defense-in-depth concern rather than a direct exploit, but worth addressing given that the keyring exists specifically to avoid storing secrets on disk.