diff --git a/codex-rs/login/src/auth/storage.rs b/codex-rs/login/src/auth/storage.rs index 2225976396..01f32d2d71 100644 --- a/codex-rs/login/src/auth/storage.rs +++ b/codex-rs/login/src/auth/storage.rs @@ -173,15 +173,29 @@ impl AuthStorageBackend for FileAuthStorage { const KEYRING_SERVICE: &str = "Codex Auth"; -// turns codex_home path into a stable, short key string +// Turns the effective auth home path into a stable, short key string. fn compute_store_key(codex_home: &Path) -> std::io::Result { - let canonical = match auth_home_from_env() { - Some(auth_home) => auth_home, - None => codex_home - .canonicalize() - .unwrap_or_else(|_| codex_home.to_path_buf()), + let home = auth_home_from_env().unwrap_or_else(|| codex_home.to_path_buf()); + Ok(compute_store_key_for_home_path(home)) +} + +fn compute_store_key_for_home_path(path: PathBuf) -> String { + let canonical = canonicalize_auth_home_path(path); + compute_store_key_for_path(&canonical) +} + +fn canonicalize_auth_home_path(path: PathBuf) -> PathBuf { + if let Ok(canonical) = path.canonicalize() { + return canonical; + } + + let (Some(parent), Some(file_name)) = (path.parent(), path.file_name()) else { + return path; }; - Ok(compute_store_key_for_path(&canonical)) + parent + .canonicalize() + .map(|parent| parent.join(file_name)) + .unwrap_or(path) } fn compute_store_key_for_path(path: &Path) -> String { diff --git a/codex-rs/login/src/auth/storage_tests.rs b/codex-rs/login/src/auth/storage_tests.rs index 135cd20cae..7f071fd9a5 100644 --- a/codex-rs/login/src/auth/storage_tests.rs +++ b/codex-rs/login/src/auth/storage_tests.rs @@ -254,13 +254,32 @@ fn auth_home_store_key_path_does_not_depend_on_directory_existing() { let root = tempdir().expect("tempdir"); let auth_home = root.path().join("missing").join("..").join("auth"); - let before_create = compute_store_key_for_path(&normalize_auth_home_path(auth_home.clone())); + let before_create = + compute_store_key_for_home_path(normalize_auth_home_path(auth_home.clone())); std::fs::create_dir_all(root.path().join("auth")).expect("create auth home"); - let after_create = compute_store_key_for_path(&normalize_auth_home_path(auth_home)); + let after_create = compute_store_key_for_home_path(normalize_auth_home_path(auth_home)); assert_eq!(before_create, after_create); } +#[cfg(unix)] +#[test] +fn auth_home_store_key_canonicalizes_symlink() -> anyhow::Result<()> { + use std::os::unix::fs::symlink; + + let root = tempdir()?; + let auth_home = root.path().join("auth"); + let auth_home_link = root.path().join("auth-link"); + std::fs::create_dir_all(&auth_home)?; + symlink(&auth_home, &auth_home_link)?; + + let canonical_key = compute_store_key_for_home_path(auth_home); + let symlink_key = compute_store_key_for_home_path(normalize_auth_home_path(auth_home_link)); + + assert_eq!(canonical_key, symlink_key); + Ok(()) +} + #[test] fn keyring_auth_storage_save_persists_and_removes_fallback_file() -> anyhow::Result<()> { let codex_home = tempdir()?;