mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
56c97e3b5c
## Why Windows Credential Manager limits generic credential blobs to 2,560 bytes. Large serialized ChatGPT auth payloads can exceed that limit, so keyring-mode CLI auth needs a backend that keeps only the encryption key in the OS keyring and stores the payload in Codex's encrypted local-secrets file. This is the third PR in the encrypted-auth stack: 1. #27504 — feature and config selection 2. #27535 — auth-specific local-secrets namespaces 3. This PR — CLI auth implementation and activation 4. MCP OAuth implementation and activation ## What Changed - Added encrypted CLI-auth storage using the `CliAuth` secrets namespace. - Preserved direct keyring storage for platforms/configurations where it remains selected. - Selected the backend consistently for login, logout, refresh, device-code login, auth loading, and login restrictions. - Threaded resolved bootstrap/full config through CLI, exec, TUI, app-server account handling, cloud config, and cloud tasks. - Removed stale `auth.json` fallback data after successful encrypted saves and removed encrypted, direct-keyring, and fallback data during logout. - Added storage and integration coverage for both direct and encrypted keyring modes. MCP OAuth persistence is intentionally left to the next PR. ## Validation - `just test -p codex-login` — 131 passed - `just test -p codex-cli` — 280 passed - `just test -p codex-app-server v2::account` — 25 passed - `just test -p codex-cloud-config service` — 21 passed, 7 skipped - `just fix -p codex-login` - `just fix -p codex-cli` - `just fmt`
72 lines
2.6 KiB
Rust
72 lines
2.6 KiB
Rust
use crate::backend::BackendBundleClient;
|
|
use crate::service::CLOUD_CONFIG_BUNDLE_TIMEOUT;
|
|
use crate::service::CloudConfigBundleService;
|
|
use codex_config::CloudConfigBundleLoadError;
|
|
use codex_config::CloudConfigBundleLoadErrorCode;
|
|
use codex_config::CloudConfigBundleLoader;
|
|
use codex_config::types::AuthCredentialsStoreMode;
|
|
use codex_login::AuthKeyringBackendKind;
|
|
use codex_login::AuthManager;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use std::sync::Mutex;
|
|
use std::sync::OnceLock;
|
|
use tokio::task::JoinHandle;
|
|
|
|
fn refresher_task_slot() -> &'static Mutex<Option<JoinHandle<()>>> {
|
|
static REFRESHER_TASK: OnceLock<Mutex<Option<JoinHandle<()>>>> = OnceLock::new();
|
|
REFRESHER_TASK.get_or_init(|| Mutex::new(None))
|
|
}
|
|
|
|
pub fn cloud_config_bundle_loader(
|
|
auth_manager: Arc<AuthManager>,
|
|
chatgpt_base_url: String,
|
|
codex_home: PathBuf,
|
|
) -> CloudConfigBundleLoader {
|
|
let service = CloudConfigBundleService::new(
|
|
auth_manager,
|
|
Arc::new(BackendBundleClient::new(chatgpt_base_url)),
|
|
codex_home,
|
|
CLOUD_CONFIG_BUNDLE_TIMEOUT,
|
|
);
|
|
let refresh_service = service.clone();
|
|
let task = tokio::spawn(async move { service.load_startup_bundle_with_timeout().await });
|
|
let refresh_task =
|
|
tokio::spawn(async move { refresh_service.refresh_cache_in_background().await });
|
|
let mut refresher_guard = refresher_task_slot().lock().unwrap_or_else(|err| {
|
|
tracing::warn!("cloud config bundle refresher task slot was poisoned");
|
|
err.into_inner()
|
|
});
|
|
if let Some(existing_task) = refresher_guard.replace(refresh_task) {
|
|
existing_task.abort();
|
|
}
|
|
CloudConfigBundleLoader::new(async move {
|
|
task.await.map_err(|err| {
|
|
tracing::error!(error = %err, "Cloud config bundle task failed");
|
|
CloudConfigBundleLoadError::new(
|
|
CloudConfigBundleLoadErrorCode::Internal,
|
|
/*status_code*/ None,
|
|
format!("cloud config bundle load failed: {err}"),
|
|
)
|
|
})?
|
|
})
|
|
}
|
|
|
|
pub async fn cloud_config_bundle_loader_for_storage(
|
|
codex_home: PathBuf,
|
|
enable_codex_api_key_env: bool,
|
|
credentials_store_mode: AuthCredentialsStoreMode,
|
|
keyring_backend_kind: AuthKeyringBackendKind,
|
|
chatgpt_base_url: String,
|
|
) -> CloudConfigBundleLoader {
|
|
let auth_manager = AuthManager::shared(
|
|
codex_home.clone(),
|
|
enable_codex_api_key_env,
|
|
credentials_store_mode,
|
|
Some(chatgpt_base_url.clone()),
|
|
keyring_backend_kind,
|
|
)
|
|
.await;
|
|
cloud_config_bundle_loader(auth_manager, chatgpt_base_url, codex_home)
|
|
}
|