Add provider-owned authentication recovery (#39274)

## What changed

- Let model providers identify recoverable authentication errors and refresh their own authentication state before falling back to `AuthManager` recovery.
- Apply provider recovery to both HTTP and WebSocket response requests, with at most one provider-owned recovery attempt per request.
- Preserve existing `401 Unauthorized` handling as the default for providers that do not customize recovery.

## Testing

- Add coverage for successful and failed provider recovery, including the single-attempt bound and preservation of the original unauthorized error.

GitOrigin-RevId: 9a737e3be96354699586f97dd88e544a45082969
This commit is contained in:
alexsong-oai authored and copyberry committed 2026-08-18 20:14:17 +00:00
1 parent f950a1ba0b
commit 22b860e80b
4 files changed
+193 -9

No files matched your search

+28
View File
@@ -7,6 +7,7 @@ use std::sync::Arc;
use codex_api::ApiError;
use codex_api::Provider;
use codex_api::SharedAuthProvider;
use codex_api::TransportError;
use codex_api::is_azure_responses_provider;
use codex_login::AuthManager;
use codex_login::CodexAuth;
@@ -71,6 +72,15 @@ pub struct ProviderAccountState {
pub requires_openai_auth: bool,
}
/// Outcome of a provider-owned attempt to recover from an authentication failure.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProviderUnauthorizedRecovery {
/// The provider has no provider-specific authentication recovery configured.
NotConfigured,
/// The provider recovered its authentication state and the request can be retried.
Recovered,
}
/// Error returned when a provider cannot construct its app-visible account state.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProviderAccountError {
@@ -160,6 +170,24 @@ pub trait ModelProvider: fmt::Debug + Send + Sync {
/// manager throughout the codebase; that is a larger refactor than this change.
fn auth_manager(&self) -> Option<Arc<AuthManager>>;
/// Returns whether this transport failure can be recovered by provider-scoped authentication.
///
/// The default preserves existing unauthorized-response handling. Providers with other
/// authentication failure shapes may recognize additional response or request-signing errors.
fn is_recoverable_auth_error(&self, error: &TransportError) -> bool {
matches!(
error,
TransportError::Http { status, .. } if *status == http::StatusCode::UNAUTHORIZED
)
}
/// Attempts provider-owned authentication recovery before using the auth manager.
fn recover_from_unauthorized(
&self,
) -> ModelProviderFuture<'_, codex_protocol::error::Result<ProviderUnauthorizedRecovery>> {
Box::pin(async { Ok(ProviderUnauthorizedRecovery::NotConfigured) })
}
/// Returns the current provider-scoped auth value, if one is configured.
fn auth(&self) -> ModelProviderFuture<'_, Option<CodexAuth>>;