mirror of
https://github.com/openai/codex.git
synced 2026-05-29 15:30:22 +00:00
[codex-cli] gate startup token refresh [ci changed_files]
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user