Default CLI auth credentials store to auto

This commit is contained in:
mikhail-oai
2026-02-16 22:10:59 -05:00
committed by Mikhail Sosonkin
parent 19afbc35c1
commit 5802e4868f
6 changed files with 78 additions and 21 deletions
+1
View File
@@ -339,6 +339,7 @@ unwrap_used = "deny"
# silence the false positive here instead of deleting a real dependency.
[workspace.metadata.cargo-shear]
ignored = [
"askama",
"icu_provider",
"openssl-sys",
"codex-utils-readiness",
+29 -2
View File
@@ -804,7 +804,13 @@ async fn external_auth_refresh_invalid_access_token_fails_turn() -> Result<()> {
#[tokio::test]
async fn login_account_api_key_succeeds_and_notifies() -> Result<()> {
let codex_home = TempDir::new()?;
create_config_toml(codex_home.path(), CreateConfigTomlParams::default())?;
create_config_toml(
codex_home.path(),
CreateConfigTomlParams {
requires_openai_auth: Some(true),
..Default::default()
},
)?;
let mut mcp = McpProcess::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
@@ -844,7 +850,28 @@ async fn login_account_api_key_succeeds_and_notifies() -> Result<()> {
};
pretty_assertions::assert_eq!(payload.auth_mode, Some(AuthMode::ApiKey));
assert!(codex_home.path().join("auth.json").exists());
drop(mcp);
let mut mcp = McpProcess::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let get_id = mcp
.send_get_account_request(GetAccountParams {
refresh_token: false,
})
.await?;
let get_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(get_id)),
)
.await??;
let account: GetAccountResponse = to_response(get_resp)?;
let expected = GetAccountResponse {
account: Some(Account::ApiKey {}),
requires_openai_auth: true,
};
assert_eq!(account, expected);
Ok(())
}
+1 -1
View File
@@ -169,4 +169,4 @@ wiremock = { workspace = true }
zstd = { workspace = true }
[package.metadata.cargo-shear]
ignored = ["openssl-sys"]
ignored = ["askama", "openssl-sys"]
+8 -8
View File
@@ -125,6 +125,13 @@
"AuthCredentialsStoreMode": {
"description": "Determine where Codex should store CLI auth credentials.",
"oneOf": [
{
"description": "Use keyring when available; otherwise, fall back to a file in CODEX_HOME.",
"enum": [
"auto"
],
"type": "string"
},
{
"description": "Persist credentials in CODEX_HOME/auth.json.",
"enum": [
@@ -139,13 +146,6 @@
],
"type": "string"
},
{
"description": "Use keyring when available; otherwise, fall back to a file in CODEX_HOME.",
"enum": [
"auto"
],
"type": "string"
},
{
"description": "Store credentials in memory only for the current process.",
"enum": [
@@ -1286,7 +1286,7 @@
}
],
"default": null,
"description": "Preferred backend for storing CLI auth credentials. file (default): Use a file in the Codex home directory. keyring: Use an OS-specific keyring service. auto: Use the keyring if available, otherwise use a file."
"description": "Preferred backend for storing CLI auth credentials. auto (default): Use the keyring if available, otherwise use a file. file: Use a file in the Codex home directory. keyring: Use an OS-specific keyring service."
},
"compact_prompt": {
"description": "Compact prompt used for history compaction.",
+33 -4
View File
@@ -29,13 +29,13 @@ use once_cell::sync::Lazy;
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum AuthCredentialsStoreMode {
/// Use keyring when available; otherwise, fall back to a file in CODEX_HOME.
#[default]
Auto,
/// Persist credentials in CODEX_HOME/auth.json.
File,
/// Persist credentials in the keyring. Fail if unavailable.
Keyring,
/// Use keyring when available; otherwise, fall back to a file in CODEX_HOME.
Auto,
/// Store credentials in memory only for the current process.
Ephemeral,
}
@@ -260,8 +260,13 @@ impl AuthStorageBackend for AutoAuthStorage {
}
fn delete(&self) -> std::io::Result<bool> {
// Keyring storage will delete from disk as well
self.keyring_storage.delete()
match self.keyring_storage.delete() {
Ok(removed) => Ok(removed),
Err(err) => {
warn!("failed to delete auth from keyring, falling back to file deletion: {err}");
self.file_storage.delete()
}
}
}
}
@@ -755,4 +760,28 @@ mod tests {
);
Ok(())
}
#[test]
fn auto_auth_storage_delete_falls_back_when_keyring_errors() -> anyhow::Result<()> {
let codex_home = tempdir()?;
let mock_keyring = MockKeyringStore::default();
let storage = AutoAuthStorage::new(
codex_home.path().to_path_buf(),
Arc::new(mock_keyring.clone()),
);
let key = compute_store_key(codex_home.path())?;
mock_keyring.set_error(&key, KeyringError::Invalid("error".into(), "delete".into()));
let auth_file = get_auth_file(codex_home.path());
std::fs::write(&auth_file, "stale")?;
let removed = storage.delete()?;
assert!(removed, "fallback deletion should report removal");
assert!(
!auth_file.exists(),
"fallback auth.json should be removed when keyring delete fails"
);
Ok(())
}
}
+6 -6
View File
@@ -261,9 +261,9 @@ pub struct Config {
pub cwd: PathBuf,
/// Preferred store for CLI auth credentials.
/// file (default): Use a file in the Codex home directory.
/// auto (default): Use the OS-specific keyring service if available, otherwise use a file.
/// file: Use a file in the Codex home directory.
/// keyring: Use an OS-specific keyring service.
/// auto: Use the OS-specific keyring service if available, otherwise use a file.
pub cli_auth_credentials_store_mode: AuthCredentialsStoreMode,
/// Definition for MCP servers that Codex can reach out to for tool calls.
@@ -923,9 +923,9 @@ pub struct ConfigToml {
pub forced_login_method: Option<ForcedLoginMethod>,
/// Preferred backend for storing CLI auth credentials.
/// file (default): Use a file in the Codex home directory.
/// auto (default): Use the keyring if available, otherwise use a file.
/// file: Use a file in the Codex home directory.
/// keyring: Use an OS-specific keyring service.
/// auto: Use the keyring if available, otherwise use a file.
#[serde(default)]
pub cli_auth_credentials_store: Option<AuthCredentialsStoreMode>,
@@ -2465,7 +2465,7 @@ trust_level = "trusted"
}
#[test]
fn config_defaults_to_file_cli_auth_store_mode() -> std::io::Result<()> {
fn config_defaults_to_auto_cli_auth_store_mode() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let cfg = ConfigToml::default();
@@ -2477,7 +2477,7 @@ trust_level = "trusted"
assert_eq!(
config.cli_auth_credentials_store_mode,
AuthCredentialsStoreMode::File,
AuthCredentialsStoreMode::Auto,
);
Ok(())