mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
[codex] preserve optional auth refresh and WIF denies [ci changed_files]
This commit is contained in:
@@ -801,6 +801,30 @@ async fn auth_result_preserves_external_auth_resolution_errors() {
|
||||
assert!(manager.auth().await.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn optional_auth_does_not_resolve_required_external_auth() {
|
||||
let cached = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let manager = AuthManager::from_auth_for_testing(cached.clone());
|
||||
manager.set_external_auth(Arc::new(FailingExternalChatgptAuth(
|
||||
"required external auth must not run",
|
||||
)));
|
||||
|
||||
let resolved = manager
|
||||
.auth_for_optional_use()
|
||||
.await
|
||||
.expect("cached ChatGPT auth should remain available");
|
||||
|
||||
assert_eq!(resolved.get_token().unwrap(), cached.get_token().unwrap());
|
||||
assert_eq!(
|
||||
manager
|
||||
.auth()
|
||||
.await
|
||||
.expect_err("normal auth should still require external resolution")
|
||||
.to_string(),
|
||||
"required external auth must not run"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn replace_non_required_external_auth_preserves_required_provider() {
|
||||
let manager = AuthManager::shared(
|
||||
|
||||
@@ -1731,6 +1731,30 @@ impl AuthManager {
|
||||
Ok(self.auth_cached())
|
||||
}
|
||||
|
||||
/// Resolves auth for optional OpenAI-backed features while another model provider is active.
|
||||
///
|
||||
/// A required external auth source belongs to the primary model provider and must not be
|
||||
/// invoked merely to initialize an optional feature. Managed ChatGPT auth and optional
|
||||
/// external auth still use the normal resolution path, including proactive token refresh.
|
||||
pub async fn auth_for_optional_use(&self) -> Option<CodexAuth> {
|
||||
if self
|
||||
.external_auth()
|
||||
.is_some_and(|external_auth| external_auth.requires_successful_resolution())
|
||||
{
|
||||
return self.auth_cached();
|
||||
}
|
||||
|
||||
match self.auth().await {
|
||||
Ok(auth) => auth,
|
||||
Err(error) => {
|
||||
// The external auth registration can race with the check above. Optional features
|
||||
// should keep using the last managed credential instead of failing startup.
|
||||
tracing::error!("Failed to resolve optional feature auth: {error}");
|
||||
self.auth_cached()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Backwards-compatible explicit name for callers that already handle auth resolution errors.
|
||||
pub async fn auth_result(&self) -> std::io::Result<Option<CodexAuth>> {
|
||||
self.auth().await
|
||||
|
||||
@@ -180,6 +180,16 @@ async fn refresh_token_refreshes_when_auth_is_unchanged() -> Result<()> {
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn auth_refreshes_when_access_token_is_near_expiry() -> Result<()> {
|
||||
assert_auth_refreshes_when_access_token_is_near_expiry(/*optional*/ false).await
|
||||
}
|
||||
|
||||
#[serial_test::serial(auth_env)]
|
||||
#[tokio::test]
|
||||
async fn optional_auth_refreshes_when_access_token_is_near_expiry() -> Result<()> {
|
||||
assert_auth_refreshes_when_access_token_is_near_expiry(/*optional*/ true).await
|
||||
}
|
||||
|
||||
async fn assert_auth_refreshes_when_access_token_is_near_expiry(optional: bool) -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = MockServer::start().await;
|
||||
@@ -208,12 +218,18 @@ async fn auth_refreshes_when_access_token_is_near_expiry() -> Result<()> {
|
||||
};
|
||||
ctx.write_auth(&initial_auth).await?;
|
||||
|
||||
let cached_auth = ctx
|
||||
.auth_manager
|
||||
.auth()
|
||||
.await
|
||||
.context("auth should resolve")?
|
||||
.context("auth should be cached")?;
|
||||
let cached_auth = if optional {
|
||||
ctx.auth_manager
|
||||
.auth_for_optional_use()
|
||||
.await
|
||||
.context("optional auth should be cached")?
|
||||
} else {
|
||||
ctx.auth_manager
|
||||
.auth()
|
||||
.await
|
||||
.context("auth should resolve")?
|
||||
.context("auth should be cached")?
|
||||
};
|
||||
|
||||
let refreshed_tokens = TokenData {
|
||||
access_token: "new-access-token".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user