Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3160d76
Add auth system proxy routing contract
canvrno-oai Jun 16, 2026
d4129e2
Fix auth manager test constructor call
canvrno-oai Jun 16, 2026
834e7f6
Fix proxy origin Clippy lint
canvrno-oai Jun 16, 2026
6ff2dca
Route plugin auth through proxy config
canvrno-oai Jun 16, 2026
8c742a8
Use issuer route for device auth proxy resolution
canvrno-oai Jun 16, 2026
860d6e5
Resolve auth proxy rebase lint
canvrno-oai Jun 17, 2026
ec45d46
Clarify proxy route class docs
canvrno-oai Jun 17, 2026
1a3e394
Merge branch 'main' into canvnro/codex-pac-pr2-contract
canvrno-oai Jun 17, 2026
18e0470
Remove unnecessary auth loader wrapper
canvrno-oai Jun 18, 2026
ea36690
Document issuer-scoped auth routing
canvrno-oai Jun 18, 2026
3970b3b
Document proxy resolution fallback
canvrno-oai Jun 18, 2026
685f361
Clarify supported default proxy path
canvrno-oai Jun 18, 2026
2aeec90
Explain sandbox proxy isolation
canvrno-oai Jun 18, 2026
a6bc4bf
Store route config in AuthConfig
canvrno-oai Jun 18, 2026
403d036
Explain auth route config ownership
canvrno-oai Jun 18, 2026
1d935d3
Cache unavailable proxy decisions briefly
canvrno-oai Jun 18, 2026
88d74eb
Match reqwest proxy variable casing
canvrno-oai Jun 18, 2026
647e94f
Minimize token endpoint routing diff
canvrno-oai Jun 18, 2026
555789b
Test enabled environment proxy routing
canvrno-oai Jun 18, 2026
1feb36b
Align PAC auth routing with current main
canvrno-oai Jun 18, 2026
4d2e988
Route Agent Identity requests through PAC config
canvrno-oai Jun 18, 2026
1ae535c
Treat empty proxy environment values as unset
canvrno-oai Jun 19, 2026
569874e
Bound system proxy decision cache
canvrno-oai Jun 22, 2026
7011be3
Merge branch 'main' into canvnro/codex-pac-pr2-contract
canvrno-oai Jun 22, 2026
7c51f51
Clarify temporary proxy decision allowance
canvrno-oai Jun 22, 2026
d7db307
Merge branch 'main' into canvnro/codex-pac-pr2-contract
canvrno-oai Jun 22, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,7 @@ async fn remote_control_start_allows_missing_auth_when_enabled() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let (transport_event_tx, _transport_event_rx) =
Expand Down Expand Up @@ -1870,6 +1871,7 @@ async fn remote_control_waits_for_account_id_before_enrolling() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let expected_server_name = gethostname().to_string_lossy().trim().to_string();
Expand Down Expand Up @@ -1966,6 +1968,7 @@ async fn persisted_enable_does_not_follow_auth_to_an_account_without_a_preferenc
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let remote_control_target =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ async fn list_remote_control_clients_recovers_auth_after_unauthorized() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
Expand Down Expand Up @@ -270,6 +271,7 @@ async fn list_remote_control_clients_retries_unauthorized_only_once() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ async fn remote_control_handle_recovers_auth_before_refreshing_pairing() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut fresh_auth = remote_control_auth_dot_json(Some("account_id"));
Expand Down Expand Up @@ -808,6 +809,7 @@ async fn remote_control_handle_discards_pairing_response_after_auth_change() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let remote_handle =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2214,6 +2214,7 @@ mod tests {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
Expand Down Expand Up @@ -2312,6 +2313,7 @@ mod tests {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
Expand Down Expand Up @@ -2435,6 +2437,7 @@ mod tests {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;
let mut auth_recovery = auth_manager.unauthorized_recovery();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ impl AccountRequestProcessor {
config.forced_chatgpt_workspace_id.clone(),
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
config.auth_route_config(),
)
};
#[cfg(debug_assertions)]
Expand Down
21 changes: 20 additions & 1 deletion codex-rs/cli/src/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_core::config::Config;
use codex_login::AuthKeyringBackendKind;
use codex_login::AuthRouteConfig;
use codex_login::CLIENT_ID;
use codex_login::CodexAuth;
use codex_login::ServerOptions;
Expand Down Expand Up @@ -119,11 +120,13 @@ async fn clear_existing_auth_before_login(
codex_home: &Path,
auth_credentials_store_mode: AuthCredentialsStoreMode,
auth_keyring_backend_kind: AuthKeyringBackendKind,
auth_route_config: Option<&AuthRouteConfig>,
) {
if let Err(err) = logout_with_revoke(
codex_home,
auth_credentials_store_mode,
auth_keyring_backend_kind,
auth_route_config,
)
.await
{
Expand All @@ -136,11 +139,13 @@ pub async fn login_with_chatgpt(
forced_chatgpt_workspace_id: Option<Vec<String>>,
cli_auth_credentials_store_mode: AuthCredentialsStoreMode,
auth_keyring_backend_kind: AuthKeyringBackendKind,
auth_route_config: Option<AuthRouteConfig>,
) -> std::io::Result<()> {
clear_existing_auth_before_login(
&codex_home,
cli_auth_credentials_store_mode,
auth_keyring_backend_kind,
auth_route_config.as_ref(),
)
.await;

Expand All @@ -150,6 +155,7 @@ pub async fn login_with_chatgpt(
forced_chatgpt_workspace_id,
cli_auth_credentials_store_mode,
auth_keyring_backend_kind,
auth_route_config,
);
let server = run_login_server(opts)?;

Expand All @@ -169,12 +175,12 @@ pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) ->
}

let forced_chatgpt_workspace_id = config.forced_chatgpt_workspace_id.clone();

match login_with_chatgpt(
config.codex_home.to_path_buf(),
forced_chatgpt_workspace_id,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
config.auth_route_config(),
)
.await
{
Expand Down Expand Up @@ -232,13 +238,15 @@ pub async fn run_login_with_access_token(
std::process::exit(1);
}

let auth_route_config = config.auth_route_config();
match login_with_access_token(
&config.codex_home,
&access_token,
config.cli_auth_credentials_store_mode,
config.forced_chatgpt_workspace_id.as_deref(),
Some(&config.chatgpt_base_url),
config.auth_keyring_backend_kind(),
auth_route_config.as_ref(),
)
.await
{
Expand Down Expand Up @@ -307,10 +315,12 @@ pub async fn run_login_with_device_code(
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
std::process::exit(1);
}
let auth_route_config = config.auth_route_config();
clear_existing_auth_before_login(
&config.codex_home,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
auth_route_config.as_ref(),
)
.await;
let forced_chatgpt_workspace_id = config.forced_chatgpt_workspace_id.clone();
Expand All @@ -320,6 +330,7 @@ pub async fn run_login_with_device_code(
forced_chatgpt_workspace_id,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
auth_route_config,
);
if let Some(iss) = issuer_base_url {
opts.issuer = iss;
Expand Down Expand Up @@ -352,10 +363,12 @@ pub async fn run_login_with_device_code_fallback_to_browser(
eprintln!("{CHATGPT_LOGIN_DISABLED_MESSAGE}");
std::process::exit(1);
}
let auth_route_config = config.auth_route_config();
clear_existing_auth_before_login(
&config.codex_home,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
auth_route_config.as_ref(),
)
.await;

Expand All @@ -366,6 +379,7 @@ pub async fn run_login_with_device_code_fallback_to_browser(
forced_chatgpt_workspace_id,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
auth_route_config,
);
if let Some(iss) = issuer_base_url {
opts.issuer = iss;
Expand Down Expand Up @@ -409,12 +423,14 @@ pub async fn run_login_with_device_code_fallback_to_browser(

pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides).await;
let auth_route_config = config.auth_route_config();

match CodexAuth::from_auth_storage(
&config.codex_home,
config.cli_auth_credentials_store_mode,
Some(&config.chatgpt_base_url),
config.auth_keyring_backend_kind(),
auth_route_config.as_ref(),
)
.await
{
Expand Down Expand Up @@ -459,11 +475,13 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {

pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides).await;
let auth_route_config = config.auth_route_config();

match logout_with_revoke(
&config.codex_home,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
auth_route_config.as_ref(),
)
.await
{
Expand Down Expand Up @@ -536,6 +554,7 @@ mod tests {
codex_home.path(),
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await;

Expand Down
10 changes: 7 additions & 3 deletions codex-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1738,9 +1738,13 @@ async fn load_exec_server_remote_auth_provider(
let agent_identity_jwt = read_codex_access_token_from_env().ok_or_else(|| {
anyhow::anyhow!("CODEX_ACCESS_TOKEN is required when --use-agent-identity-auth is set")
})?;
let auth =
CodexAuth::from_agent_identity_jwt(&agent_identity_jwt, Some(&config.chatgpt_base_url))
.await?;
let auth_route_config = config.auth_route_config();
let auth = CodexAuth::from_agent_identity_jwt(
&agent_identity_jwt,
Some(&config.chatgpt_base_url),
auth_route_config.as_ref(),
)
.await?;
return Ok(codex_model_provider::auth_provider_from_auth(&auth));
}

Expand Down
2 changes: 2 additions & 0 deletions codex-rs/cli/src/plugin_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,13 @@ pub(crate) async fn load_cli_auth_mode(config: &Config) -> Option<AuthMode> {
return Some(CodexAuth::from_api_key(&api_key).api_auth_mode());
}

let auth_route_config = config.auth_route_config();
CodexAuth::from_auth_storage(
&config.codex_home,
config.cli_auth_credentials_store_mode,
Some(&config.chatgpt_base_url),
config.auth_keyring_backend_kind(),
auth_route_config.as_ref(),
)
.await
.ok()
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/cloud-config/src/bundle_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use codex_config::CloudConfigBundleLoader;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthKeyringBackendKind;
use codex_login::AuthManager;
use codex_login::AuthRouteConfig;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
Expand Down Expand Up @@ -58,6 +59,7 @@ pub async fn cloud_config_bundle_loader_for_storage(
credentials_store_mode: AuthCredentialsStoreMode,
keyring_backend_kind: AuthKeyringBackendKind,
chatgpt_base_url: String,
auth_route_config: Option<AuthRouteConfig>,
) -> CloudConfigBundleLoader {
let auth_manager = AuthManager::shared(
codex_home.clone(),
Expand All @@ -66,6 +68,7 @@ pub async fn cloud_config_bundle_loader_for_storage(
/*forced_chatgpt_workspace_id*/ None,
Some(chatgpt_base_url.clone()),
keyring_backend_kind,
auth_route_config,
Comment thread
canvrno-oai marked this conversation as resolved.
)
.await;
cloud_config_bundle_loader(auth_manager, chatgpt_base_url, codex_home)
Expand Down
7 changes: 7 additions & 0 deletions codex-rs/cloud-config/src/service_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ async fn auth_manager_with_api_key() -> Arc<AuthManager> {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await,
)
Expand Down Expand Up @@ -83,6 +84,7 @@ async fn auth_manager_with_plan_and_identity(
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await,
)
Expand All @@ -108,6 +110,7 @@ async fn auth_manager_with_agent_identity_business_plan() -> Arc<AuthManager> {
task_id: Some("task-123".to_string()),
},
"https://auth.openai.com/api/accounts",
/*auth_route_config*/ None,
)
.await
.expect("agent identity record should be complete"),
Expand Down Expand Up @@ -686,6 +689,7 @@ async fn get_bundle_recovers_after_unauthorized_reload() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await,
);
Expand Down Expand Up @@ -742,6 +746,7 @@ async fn get_bundle_recovers_after_unauthorized_reload_updates_cache_identity()
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await,
);
Expand Down Expand Up @@ -806,6 +811,7 @@ async fn get_bundle_surfaces_auth_recovery_message() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await,
);
Expand Down Expand Up @@ -872,6 +878,7 @@ async fn get_bundle_unauthorized_without_recovery_uses_generic_message() {
/*forced_chatgpt_workspace_id*/ None,
/*chatgpt_base_url*/ None,
AuthKeyringBackendKind::default(),
/*auth_route_config*/ None,
)
.await,
);
Expand Down
1 change: 1 addition & 0 deletions codex-rs/cloud-tasks/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub async fn load_auth_manager(chatgpt_base_url: Option<String>) -> Option<AuthM
config.forced_chatgpt_workspace_id.clone(),
chatgpt_base_url.or(Some(config.chatgpt_base_url.clone())),
config.auth_keyring_backend_kind(),
config.auth_route_config(),
Comment thread
canvrno-oai marked this conversation as resolved.
)
.await,
)
Expand Down
6 changes: 6 additions & 0 deletions codex-rs/codex-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod chatgpt_hosts;
mod custom_ca;
mod default_client;
mod error;
mod outbound_proxy;
mod request;
mod retry;
mod sse;
Expand All @@ -25,6 +26,11 @@ pub use crate::default_client::CodexHttpClient;
pub use crate::default_client::CodexRequestBuilder;
pub use crate::error::StreamError;
pub use crate::error::TransportError;
pub use crate::outbound_proxy::BuildRouteAwareHttpClientError;
pub use crate::outbound_proxy::ClientRouteClass;
pub use crate::outbound_proxy::OutboundProxyConfig;
pub use crate::outbound_proxy::RouteFailureClass;
pub use crate::outbound_proxy::build_reqwest_client_for_route;
pub use crate::request::EncodedJsonBody;
pub use crate::request::PreparedRequestBody;
pub use crate::request::Request;
Expand Down
Loading
Loading