mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
Expose auth refresh failure reason
This commit is contained in:
@@ -2,5 +2,6 @@
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AuthMode } from "./AuthMode";
|
||||
import type { RefreshTokenFailedReason } from "./RefreshTokenFailedReason";
|
||||
|
||||
export type GetAuthStatusResponse = { authMethod: AuthMode | null, authToken: string | null, requiresOpenaiAuth: boolean | null, };
|
||||
export type GetAuthStatusResponse = { authMethod: AuthMode | null, authToken: string | null, refreshFailureReason: RefreshTokenFailedReason | null, requiresOpenaiAuth: boolean | null, };
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type RefreshTokenFailedReason = "expired" | "exhausted" | "revoked" | "other";
|
||||
@@ -61,6 +61,7 @@ export type { ReasoningEffort } from "./ReasoningEffort";
|
||||
export type { ReasoningItemContent } from "./ReasoningItemContent";
|
||||
export type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary";
|
||||
export type { ReasoningSummary } from "./ReasoningSummary";
|
||||
export type { RefreshTokenFailedReason } from "./RefreshTokenFailedReason";
|
||||
export type { RequestId } from "./RequestId";
|
||||
export type { Resource } from "./Resource";
|
||||
export type { ResourceContent } from "./ResourceContent";
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::auth::RefreshTokenFailedReason;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
@@ -193,6 +194,7 @@ pub struct ExecOneOffCommandParams {
|
||||
pub struct GetAuthStatusResponse {
|
||||
pub auth_method: Option<AuthMode>,
|
||||
pub auth_token: Option<String>,
|
||||
pub refresh_failure_reason: Option<RefreshTokenFailedReason>,
|
||||
pub requires_openai_auth: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -371,6 +371,7 @@ use codex_memories_write::clear_memory_roots_contents;
|
||||
use codex_model_provider::create_model_provider;
|
||||
use codex_models_manager::collaboration_mode_presets::builtin_collaboration_mode_presets;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::auth::RefreshTokenFailedReason;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::Personality;
|
||||
|
||||
@@ -45,7 +45,16 @@ enum CancelLoginError {
|
||||
enum RefreshTokenRequestOutcome {
|
||||
NotAttemptedOrSucceeded,
|
||||
FailedTransiently,
|
||||
FailedPermanently,
|
||||
FailedPermanently(RefreshTokenFailedReason),
|
||||
}
|
||||
|
||||
impl RefreshTokenRequestOutcome {
|
||||
fn failed_reason(&self) -> Option<RefreshTokenFailedReason> {
|
||||
match self {
|
||||
Self::NotAttemptedOrSucceeded | Self::FailedTransiently => None,
|
||||
Self::FailedPermanently(reason) => Some(*reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ActiveLogin {
|
||||
@@ -744,12 +753,11 @@ impl AccountRequestProcessor {
|
||||
return RefreshTokenRequestOutcome::NotAttemptedOrSucceeded;
|
||||
}
|
||||
if do_refresh && let Err(err) = self.auth_manager.refresh_token().await {
|
||||
let failed_reason = err.failed_reason();
|
||||
if failed_reason.is_none() {
|
||||
tracing::warn!("failed to refresh token while getting account: {err}");
|
||||
return RefreshTokenRequestOutcome::FailedTransiently;
|
||||
if let Some(reason) = err.failed_reason() {
|
||||
return RefreshTokenRequestOutcome::FailedPermanently(reason);
|
||||
}
|
||||
return RefreshTokenRequestOutcome::FailedPermanently;
|
||||
tracing::warn!("failed to refresh token while getting account: {err}");
|
||||
return RefreshTokenRequestOutcome::FailedTransiently;
|
||||
}
|
||||
RefreshTokenRequestOutcome::NotAttemptedOrSucceeded
|
||||
}
|
||||
@@ -761,7 +769,7 @@ impl AccountRequestProcessor {
|
||||
let include_token = params.include_token.unwrap_or(false);
|
||||
let do_refresh = params.refresh_token.unwrap_or(false);
|
||||
|
||||
self.refresh_token_if_requested(do_refresh).await;
|
||||
let refresh_outcome = self.refresh_token_if_requested(do_refresh).await;
|
||||
|
||||
// Determine whether auth is required based on the active model provider.
|
||||
// If a custom provider is configured with `requires_openai_auth == false`,
|
||||
@@ -772,6 +780,7 @@ impl AccountRequestProcessor {
|
||||
GetAuthStatusResponse {
|
||||
auth_method: None,
|
||||
auth_token: None,
|
||||
refresh_failure_reason: None,
|
||||
requires_openai_auth: Some(false),
|
||||
}
|
||||
} else {
|
||||
@@ -782,14 +791,17 @@ impl AccountRequestProcessor {
|
||||
};
|
||||
match auth {
|
||||
Some(auth) => {
|
||||
let permanent_refresh_failure =
|
||||
self.auth_manager.refresh_failure_for_auth(&auth).is_some();
|
||||
let refresh_failure_reason = refresh_outcome.failed_reason().or_else(|| {
|
||||
self.auth_manager
|
||||
.refresh_failure_for_auth(&auth)
|
||||
.map(|failure| failure.reason)
|
||||
});
|
||||
let auth_mode = auth.api_auth_mode();
|
||||
let (reported_auth_method, token_opt) = if matches!(
|
||||
auth,
|
||||
CodexAuth::AgentIdentity(_) | CodexAuth::PersonalAccessToken(_)
|
||||
) || include_token
|
||||
&& permanent_refresh_failure
|
||||
&& refresh_failure_reason.is_some()
|
||||
{
|
||||
// This response cannot represent the metadata needed to reuse these
|
||||
// credentials.
|
||||
@@ -810,12 +822,14 @@ impl AccountRequestProcessor {
|
||||
GetAuthStatusResponse {
|
||||
auth_method: reported_auth_method,
|
||||
auth_token: token_opt,
|
||||
refresh_failure_reason,
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
}
|
||||
None => GetAuthStatusResponse {
|
||||
auth_method: None,
|
||||
auth_token: None,
|
||||
refresh_failure_reason: None,
|
||||
requires_openai_auth: Some(true),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use codex_app_server_protocol::LoginAccountResponse;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_config::types::AuthCredentialsStoreMode;
|
||||
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
|
||||
use codex_protocol::auth::RefreshTokenFailedReason;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
@@ -211,6 +212,7 @@ async fn get_auth_status_with_personal_access_token_omits_token() -> Result<()>
|
||||
GetAuthStatusResponse {
|
||||
auth_method: Some(AuthMode::PersonalAccessToken),
|
||||
auth_token: None,
|
||||
refresh_failure_reason: None,
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
);
|
||||
@@ -308,6 +310,7 @@ async fn get_auth_status_with_api_key_refresh_requested() -> Result<()> {
|
||||
GetAuthStatusResponse {
|
||||
auth_method: Some(AuthMode::ApiKey),
|
||||
auth_token: Some("sk-test-key".to_string()),
|
||||
refresh_failure_reason: None,
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
);
|
||||
@@ -372,6 +375,7 @@ async fn get_auth_status_omits_token_after_permanent_refresh_failure() -> Result
|
||||
GetAuthStatusResponse {
|
||||
auth_method: Some(AuthMode::Chatgpt),
|
||||
auth_token: None,
|
||||
refresh_failure_reason: Some(RefreshTokenFailedReason::Exhausted),
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
);
|
||||
@@ -454,6 +458,7 @@ async fn get_auth_status_omits_token_after_proactive_refresh_failure() -> Result
|
||||
GetAuthStatusResponse {
|
||||
auth_method: Some(AuthMode::Chatgpt),
|
||||
auth_token: None,
|
||||
refresh_failure_reason: Some(RefreshTokenFailedReason::Exhausted),
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
);
|
||||
@@ -521,6 +526,7 @@ async fn get_auth_status_returns_token_after_proactive_refresh_recovery() -> Res
|
||||
GetAuthStatusResponse {
|
||||
auth_method: Some(AuthMode::Chatgpt),
|
||||
auth_token: None,
|
||||
refresh_failure_reason: Some(RefreshTokenFailedReason::Exhausted),
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
);
|
||||
@@ -554,6 +560,7 @@ async fn get_auth_status_returns_token_after_proactive_refresh_recovery() -> Res
|
||||
GetAuthStatusResponse {
|
||||
auth_method: Some(AuthMode::Chatgpt),
|
||||
auth_token: Some("recovered-access-token".to_string()),
|
||||
refresh_failure_reason: None,
|
||||
requires_openai_auth: Some(true),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -19,8 +19,6 @@ use codex_app_server_protocol::ChatgptAuthTokensRefreshReason;
|
||||
use codex_app_server_protocol::ChatgptAuthTokensRefreshResponse;
|
||||
use codex_app_server_protocol::GetAccountParams;
|
||||
use codex_app_server_protocol::GetAccountResponse;
|
||||
use codex_app_server_protocol::GetAuthStatusParams;
|
||||
use codex_app_server_protocol::GetAuthStatusResponse;
|
||||
use codex_app_server_protocol::JSONRPCError;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::JSONRPCNotification;
|
||||
@@ -1771,22 +1769,9 @@ async fn get_account_omits_chatgpt_after_permanent_refresh_failure() -> Result<(
|
||||
.await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let auth_status_request_id = mcp
|
||||
.send_get_auth_status_request(GetAuthStatusParams {
|
||||
include_token: Some(true),
|
||||
refresh_token: Some(true),
|
||||
})
|
||||
.await?;
|
||||
let auth_status_resp: JSONRPCResponse = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_response_message(RequestId::Integer(auth_status_request_id)),
|
||||
)
|
||||
.await??;
|
||||
let _: GetAuthStatusResponse = to_response(auth_status_resp)?;
|
||||
|
||||
let request_id = mcp
|
||||
.send_get_account_request(GetAccountParams {
|
||||
refresh_token: false,
|
||||
refresh_token: true,
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
@@ -112,7 +114,8 @@ impl RefreshTokenFailedError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum RefreshTokenFailedReason {
|
||||
Expired,
|
||||
Exhausted,
|
||||
|
||||
Reference in New Issue
Block a user