Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use codex_config::types::AuthCredentialsStoreMode;
use codex_core::test_support::auth_manager_from_auth;
use codex_core::test_support::auth_manager_from_auth_with_home;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_login::save_auth;
Expand Down Expand Up @@ -743,6 +744,7 @@ async fn remote_control_start_allows_missing_auth_when_enabled() {
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let (transport_event_tx, _transport_event_rx) =
Expand Down Expand Up @@ -1544,6 +1546,7 @@ async fn remote_control_waits_for_account_id_before_enrolling() {
codex_home.path(),
&remote_control_auth_dot_json(/*account_id*/ None),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("auth without account id should save");
let state_db = remote_control_state_runtime(&codex_home).await;
Expand All @@ -1552,6 +1555,7 @@ async fn remote_control_waits_for_account_id_before_enrolling() {
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let expected_server_name = gethostname().to_string_lossy().trim().to_string();
Expand Down Expand Up @@ -1593,6 +1597,7 @@ async fn remote_control_waits_for_account_id_before_enrolling() {
codex_home.path(),
&remote_control_auth_dot_json(Some("account_id")),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("auth with account id should save");
auth_manager.reload().await;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use codex_app_server_protocol::RemoteControlClientsListParams;
use codex_app_server_protocol::RemoteControlClientsListResponse;
use codex_app_server_protocol::RemoteControlClientsRevokeParams;
use codex_app_server_protocol::RemoteControlClientsRevokeResponse;
use codex_login::AuthKeyringBackendKind;
use pretty_assertions::assert_eq;

fn client_management_handle(
Expand Down Expand Up @@ -172,13 +173,15 @@ async fn list_remote_control_clients_recovers_auth_after_unauthorized() {
codex_home.path(),
&stale_auth,
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("stale auth should save");
let auth_manager = AuthManager::shared(
codex_home.path().to_path_buf(),
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
Expand All @@ -191,6 +194,7 @@ async fn list_remote_control_clients_recovers_auth_after_unauthorized() {
codex_home.path(),
&fresh_auth,
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("fresh auth should save");

Expand Down Expand Up @@ -253,13 +257,15 @@ async fn list_remote_control_clients_retries_unauthorized_only_once() {
codex_home.path(),
&stale_auth,
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("stale auth should save");
let auth_manager = AuthManager::shared(
codex_home.path().to_path_buf(),
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
Expand All @@ -272,6 +278,7 @@ async fn list_remote_control_clients_retries_unauthorized_only_once() {
codex_home.path(),
&fresh_auth,
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("fresh auth should save");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::super::protocol::RemoteControlPairingStatusRequest;
use super::super::protocol::StartRemoteControlPairingRequest;
use super::*;
use codex_login::AuthKeyringBackendKind;
use pretty_assertions::assert_eq;
use std::io;

Expand Down Expand Up @@ -528,13 +529,15 @@ async fn remote_control_handle_recovers_auth_before_refreshing_pairing() {
codex_home.path(),
&stale_auth,
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("stale auth should save");
let auth_manager = AuthManager::shared(
codex_home.path().to_path_buf(),
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
Expand All @@ -547,6 +550,7 @@ async fn remote_control_handle_recovers_auth_before_refreshing_pairing() {
codex_home.path(),
&fresh_auth,
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("fresh auth should save");
let remote_handle =
Expand Down Expand Up @@ -784,13 +788,15 @@ async fn remote_control_handle_discards_pairing_response_after_auth_change() {
codex_home.path(),
&remote_control_auth_dot_json(Some("account_id")),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("initial auth should save");
let auth_manager = AuthManager::shared(
codex_home.path().to_path_buf(),
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let remote_handle =
Expand All @@ -812,6 +818,7 @@ async fn remote_control_handle_discards_pairing_response_after_auth_change() {
codex_home.path(),
&remote_control_auth_dot_json(Some("next_account_id")),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("next auth should save");
auth_manager.reload().await;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,7 @@ mod tests {
use codex_config::types::AuthCredentialsStoreMode;
use codex_core::test_support::auth_manager_from_auth;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
use codex_login::CodexAuth;
use codex_login::save_auth;
use codex_login::token_data::TokenData;
Expand Down Expand Up @@ -2006,6 +2007,7 @@ mod tests {
codex_home.path(),
&remote_control_auth_dot_json("stale-token"),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("stale auth should save");
let state_db = remote_control_state_runtime(&codex_home).await;
Expand All @@ -2014,6 +2016,7 @@ mod tests {
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
Expand All @@ -2024,6 +2027,7 @@ mod tests {
codex_home.path(),
&remote_control_auth_dot_json("fresh-token"),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("fresh auth should save");

Expand Down Expand Up @@ -2098,6 +2102,7 @@ mod tests {
codex_home.path(),
&remote_control_auth_dot_json("stale-token"),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("stale auth should save");
let state_db = remote_control_state_runtime(&codex_home).await;
Expand All @@ -2106,6 +2111,7 @@ mod tests {
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
Expand All @@ -2118,6 +2124,7 @@ mod tests {
codex_home.path(),
&remote_control_auth_dot_json("fresh-token"),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)
.expect("fresh auth should save");

Expand Down Expand Up @@ -2222,6 +2229,7 @@ mod tests {
/*enable_codex_api_key_env*/ false,
AuthCredentialsStoreMode::File,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
Expand Down
40 changes: 40 additions & 0 deletions codex-rs/app-server/src/mcp_refresh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ async fn build_refresh_config(
config.mcp_oauth_credentials_store_mode,
)
.map_err(io::Error::other)?,
auth_keyring_backend_kind: serde_json::to_value(config.auth_keyring_backend_kind())
.map_err(io::Error::other)?,
})
}

Expand Down Expand Up @@ -108,6 +110,7 @@ mod tests {
use codex_config::ThreadConfigLoadErrorCode;
use codex_config::ThreadConfigLoader;
use codex_config::ThreadConfigSource;
use codex_config::types::AuthKeyringBackendKind;
use codex_core::config::ConfigOverrides;
use codex_core::init_state_db;
use codex_core::thread_store_from_config;
Expand Down Expand Up @@ -145,6 +148,39 @@ mod tests {
Ok(())
}

#[tokio::test]
async fn refresh_config_uses_latest_auth_keyring_backend() -> anyhow::Result<()> {
let (temp_dir, thread_manager, config_manager, _loader) = refresh_test_state().await?;
std::fs::write(
temp_dir.path().join(codex_config::CONFIG_TOML_FILE),
"[features]\nsecret_auth_storage = true\n",
)?;

let mut good_thread = None;
for thread_id in thread_manager.list_thread_ids().await {
let thread = thread_manager.get_thread(thread_id).await?;
let thread_config = thread.config().await;
if thread_config.cwd.ends_with("good") {
good_thread = Some((thread, thread_config));
break;
}
}
let (thread, thread_config) = good_thread.expect("good test thread should exist");

let refresh_config =
build_refresh_config(&thread_manager, &config_manager, thread_config).await?;
let backend = serde_json::from_value::<AuthKeyringBackendKind>(
refresh_config.auth_keyring_backend_kind,
)?;

assert_eq!(
thread.config().await.auth_keyring_backend_kind(),
AuthKeyringBackendKind::Direct
);
assert_eq!(backend, AuthKeyringBackendKind::Secrets);
Ok(())
}

async fn refresh_test_state() -> anyhow::Result<(
TempDir,
Arc<ThreadManager>,
Expand All @@ -156,6 +192,10 @@ mod tests {
let bad_cwd = temp_dir.path().join("bad");
std::fs::create_dir_all(&good_cwd)?;
std::fs::create_dir_all(&bad_cwd)?;
std::fs::write(
temp_dir.path().join(codex_config::CONFIG_TOML_FILE),
"[features]\nsecret_auth_storage = false\n",
)?;

let initial_config_manager =
ConfigManager::without_managed_config_for_tests(temp_dir.path().to_path_buf());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ impl AccountRequestProcessor {
&self.config.codex_home,
&params.api_key,
self.config.cli_auth_credentials_store_mode,
self.config.auth_keyring_backend_kind(),
) {
Ok(()) => {
self.auth_manager.reload().await;
Expand Down Expand Up @@ -334,6 +335,7 @@ impl AccountRequestProcessor {
CLIENT_ID.to_string(),
config.forced_chatgpt_workspace_id.clone(),
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
)
};
#[cfg(debug_assertions)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ impl McpRequestProcessor {
&name,
&url,
config.mcp_oauth_credentials_store_mode,
config.auth_keyring_backend_kind(),
http_headers,
env_http_headers,
&resolved_scopes.scopes,
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/app-server/src/request_processors/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,7 @@ impl PluginRequestProcessor {
);

let store_mode = config.mcp_oauth_credentials_store_mode;
let keyring_backend_kind = config.auth_keyring_backend_kind();
let callback_port = config.mcp_oauth_callback_port;
let callback_url = config.mcp_oauth_callback_url.clone();
let outgoing = Arc::clone(&self.outgoing);
Expand All @@ -1688,6 +1689,7 @@ impl PluginRequestProcessor {
&name,
&oauth_config.url,
store_mode,
keyring_backend_kind,
oauth_config.http_headers.clone(),
oauth_config.env_http_headers.clone(),
&resolved_scopes.scopes,
Expand All @@ -1704,6 +1706,7 @@ impl PluginRequestProcessor {
&name,
&oauth_config.url,
store_mode,
keyring_backend_kind,
oauth_config.http_headers,
oauth_config.env_http_headers,
&[],
Expand Down
9 changes: 8 additions & 1 deletion codex-rs/app-server/tests/common/auth_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
use codex_login::save_auth;
use codex_login::token_data::TokenData;
use codex_login::token_data::parse_chatgpt_jwt_claims;
Expand Down Expand Up @@ -167,5 +168,11 @@ pub fn write_chatgpt_auth(
personal_access_token: None,
};

save_auth(codex_home, &auth, cli_auth_credentials_store_mode).context("write auth.json")
save_auth(
codex_home,
&auth,
cli_auth_credentials_store_mode,
AuthKeyringBackendKind::default(),
)
.context("write auth.json")
}
2 changes: 2 additions & 0 deletions codex-rs/app-server/tests/suite/v2/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use codex_app_server_protocol::ServerRequest;
use codex_app_server_protocol::TurnCompletedNotification;
use codex_app_server_protocol::TurnStatus;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthKeyringBackendKind;
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
use codex_login::login_with_api_key;
use codex_protocol::account::PlanType as AccountPlanType;
Expand Down Expand Up @@ -198,6 +199,7 @@ async fn logout_account_removes_auth_and_notifies() -> Result<()> {
codex_home.path(),
"sk-test-key",
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)?;
assert!(codex_home.path().join("auth.json").exists());

Expand Down
Loading
Loading