[codex-cli] gate startup token refresh [ci changed_files]

This commit is contained in:
Cooper Gamble
2026-05-21 22:08:27 +00:00
parent 445c687cdf
commit d6c7bfd44f
4 changed files with 92 additions and 15 deletions

View File

@@ -456,13 +456,6 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
}
set_default_client_residency_requirement(config.enforce_residency.value());
refresh_managed_chatgpt_token_for_storage_if_near_expiry(
config.codex_home.to_path_buf(),
/*enable_codex_api_key_env*/ false,
config.cli_auth_credentials_store_mode,
config.chatgpt_base_url.clone(),
)
.await;
if let Err(err) = enforce_login_restrictions(&AuthConfig {
codex_home: config.codex_home.to_path_buf(),
@@ -477,6 +470,14 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
std::process::exit(1);
}
refresh_managed_chatgpt_token_for_storage_if_near_expiry(
config.codex_home.to_path_buf(),
/*enable_codex_api_key_env*/ true,
config.cli_auth_credentials_store_mode,
config.chatgpt_base_url.clone(),
)
.await;
let otel = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
codex_core::otel_init::build_provider(
&config,

View File

@@ -1751,13 +1751,13 @@ impl AuthManager {
return Ok(());
}
let _refresh_lock = self.acquire_chatgpt_startup_refresh_lock().await?;
let _refresh_guard = self.refresh_lock.acquire().await.map_err(|_| {
RefreshTokenError::Permanent(RefreshTokenFailedError::new(
RefreshTokenFailedReason::Other,
REFRESH_TOKEN_UNKNOWN_MESSAGE.to_string(),
))
})?;
let _refresh_lock = self.acquire_chatgpt_startup_refresh_lock().await?;
self.refresh_token_with_refresh_lock_held().await
}

View File

@@ -320,6 +320,81 @@ async fn refresh_managed_chatgpt_token_waits_while_startup_refresh_lock_is_held(
Ok(())
}
#[serial_test::serial(auth_refresh)]
#[tokio::test]
async fn refresh_token_does_not_wait_while_startup_refresh_lock_is_held() -> Result<()> {
skip_if_no_network!(Ok(()));
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/oauth/token"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"access_token": "new-access-token",
"refresh_token": "new-refresh-token"
})))
.expect(1)
.mount(&server)
.await;
let ctx = RefreshTokenTestContext::new(&server).await?;
let initial_last_refresh = Utc::now();
let expired_access_token = access_token_with_expiration(Utc::now() - Duration::minutes(1));
let initial_tokens = build_tokens(&expired_access_token, INITIAL_REFRESH_TOKEN);
ctx.write_auth(&AuthDotJson {
auth_mode: Some(AuthMode::Chatgpt),
openai_api_key: None,
tokens: Some(initial_tokens),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
})
.await?;
let lock_path = ctx
.codex_home
.path()
.join("chatgpt-access-token-startup-refresh.lock");
let lock_file = File::options()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(lock_path)?;
lock_file.try_lock()?;
let auth_manager = Arc::clone(&ctx.auth_manager);
let startup_refresh_task = tokio::spawn(async move {
auth_manager
.refresh_managed_chatgpt_token_if_near_expiry()
.await
});
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
assert!(
!startup_refresh_task.is_finished(),
"startup refresh should wait while another process holds the file lock"
);
tokio::time::timeout(
std::time::Duration::from_secs(1),
ctx.auth_manager.refresh_token_from_authority(),
)
.await
.context("normal refresh should not wait for the startup file lock")?
.context("normal refresh should succeed")?;
startup_refresh_task.abort();
let _ = startup_refresh_task.await;
drop(lock_file);
let stored = ctx.load_auth()?;
let tokens = stored.tokens.as_ref().context("tokens should exist")?;
assert_eq!(tokens.access_token, "new-access-token");
assert_eq!(tokens.refresh_token, "new-refresh-token");
server.verify().await;
Ok(())
}
#[serial_test::serial(auth_refresh)]
#[tokio::test]
async fn refresh_token_skips_refresh_when_auth_changed() -> Result<()> {

View File

@@ -1141,13 +1141,6 @@ pub async fn run_main(
}
set_default_client_residency_requirement(config.enforce_residency.value());
refresh_managed_chatgpt_token_for_storage_if_near_expiry(
config.codex_home.to_path_buf(),
/*enable_codex_api_key_env*/ false,
config.cli_auth_credentials_store_mode,
config.chatgpt_base_url.clone(),
)
.await;
if let Some(warning) = add_dir_warning_message(
&cli.add_dir,
@@ -1175,6 +1168,14 @@ pub async fn run_main(
eprintln!("{err}");
std::process::exit(1);
}
refresh_managed_chatgpt_token_for_storage_if_near_expiry(
config.codex_home.to_path_buf(),
/*enable_codex_api_key_env*/ false,
config.cli_auth_credentials_store_mode,
config.chatgpt_base_url.clone(),
)
.await;
}
let log_dir = config.log_dir.clone();