From 50c8c745eb86090822ce1035852865f12d6b8746 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Wed, 18 Mar 2026 15:16:22 -0700 Subject: [PATCH] auth --- codex-rs/Cargo.lock | 7 ++++ codex-rs/Cargo.toml | 2 + codex-rs/core/src/auth.rs | 70 +++---------------------------- codex-rs/core/src/auth/storage.rs | 52 +++++++++++++++++++++++ 4 files changed, 67 insertions(+), 64 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 771b714e0a..f56968430e 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1935,6 +1935,13 @@ dependencies = [ "zstd", ] +[[package]] +name = "codex-core-auth" +version = "0.0.0" +dependencies = [ + "codex-core", +] + [[package]] name = "codex-debug-client" version = "0.0.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 35ff64195e..8ca311cf64 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -22,6 +22,7 @@ members = [ "shell-escalation", "skills", "core", + "core/auth", "environment", "hooks", "secrets", @@ -104,6 +105,7 @@ codex-cloud-requirements = { path = "cloud-requirements" } codex-connectors = { path = "connectors" } codex-config = { path = "config" } codex-core = { path = "core" } +codex-core-auth = { path = "core/auth" } codex-environment = { path = "environment" } codex-exec = { path = "exec" } codex-execpolicy = { path = "execpolicy" } diff --git a/codex-rs/core/src/auth.rs b/codex-rs/core/src/auth.rs index 90f0dcfdaf..61af6c857d 100644 --- a/codex-rs/core/src/auth.rs +++ b/codex-rs/core/src/auth.rs @@ -27,7 +27,6 @@ use crate::config::Config; use crate::error::RefreshTokenFailedError; use crate::error::RefreshTokenFailedReason; use crate::token_data::KnownPlan as InternalKnownPlan; -use crate::token_data::PlanType as InternalPlanType; use crate::token_data::TokenData; use crate::token_data::parse_chatgpt_jwt_claims; use crate::util::try_parse_error_message; @@ -752,67 +751,6 @@ fn refresh_token_endpoint() -> String { .unwrap_or_else(|_| REFRESH_TOKEN_URL.to_string()) } -impl AuthDotJson { - fn from_external_tokens(external: &ExternalAuthTokens) -> std::io::Result { - let mut token_info = - parse_chatgpt_jwt_claims(&external.access_token).map_err(std::io::Error::other)?; - token_info.chatgpt_account_id = Some(external.chatgpt_account_id.clone()); - token_info.chatgpt_plan_type = external - .chatgpt_plan_type - .as_deref() - .map(InternalPlanType::from_raw_value) - .or(token_info.chatgpt_plan_type) - .or(Some(InternalPlanType::Unknown("unknown".to_string()))); - let tokens = TokenData { - id_token: token_info, - access_token: external.access_token.clone(), - refresh_token: String::new(), - account_id: Some(external.chatgpt_account_id.clone()), - }; - - Ok(Self { - auth_mode: Some(ApiAuthMode::ChatgptAuthTokens), - openai_api_key: None, - tokens: Some(tokens), - last_refresh: Some(Utc::now()), - }) - } - - fn from_external_access_token( - access_token: &str, - chatgpt_account_id: &str, - chatgpt_plan_type: Option<&str>, - ) -> std::io::Result { - let external = ExternalAuthTokens { - access_token: access_token.to_string(), - chatgpt_account_id: chatgpt_account_id.to_string(), - chatgpt_plan_type: chatgpt_plan_type.map(str::to_string), - }; - Self::from_external_tokens(&external) - } - - fn resolved_mode(&self) -> ApiAuthMode { - if let Some(mode) = self.auth_mode { - return mode; - } - if self.openai_api_key.is_some() { - return ApiAuthMode::ApiKey; - } - ApiAuthMode::Chatgpt - } - - fn storage_mode( - &self, - auth_credentials_store_mode: AuthCredentialsStoreMode, - ) -> AuthCredentialsStoreMode { - if self.resolved_mode() == ApiAuthMode::ChatgptAuthTokens { - AuthCredentialsStoreMode::Ephemeral - } else { - auth_credentials_store_mode - } - } -} - /// Internal cached auth state. #[derive(Clone)] struct CachedAuth { @@ -1412,8 +1350,12 @@ impl AuthManager { ), ))); } - let auth_dot_json = - AuthDotJson::from_external_tokens(&refreshed).map_err(RefreshTokenError::Transient)?; + let auth_dot_json = AuthDotJson::from_external_access_token( + &refreshed.access_token, + &refreshed.chatgpt_account_id, + refreshed.chatgpt_plan_type.as_deref(), + ) + .map_err(RefreshTokenError::Transient)?; save_auth( &self.codex_home, &auth_dot_json, diff --git a/codex-rs/core/src/auth/storage.rs b/codex-rs/core/src/auth/storage.rs index b1e04b8685..8959486280 100644 --- a/codex-rs/core/src/auth/storage.rs +++ b/codex-rs/core/src/auth/storage.rs @@ -19,7 +19,9 @@ use std::sync::Arc; use std::sync::Mutex; use tracing::warn; +use crate::token_data::PlanType; use crate::token_data::TokenData; +use crate::token_data::parse_chatgpt_jwt_claims; use codex_app_server_protocol::AuthMode; use codex_keyring_store::DefaultKeyringStore; use codex_keyring_store::KeyringStore; @@ -56,6 +58,56 @@ pub struct AuthDotJson { pub last_refresh: Option>, } +impl AuthDotJson { + pub(super) fn from_external_access_token( + access_token: &str, + chatgpt_account_id: &str, + chatgpt_plan_type: Option<&str>, + ) -> std::io::Result { + let mut token_info = + parse_chatgpt_jwt_claims(access_token).map_err(std::io::Error::other)?; + token_info.chatgpt_account_id = Some(chatgpt_account_id.to_string()); + token_info.chatgpt_plan_type = chatgpt_plan_type + .map(PlanType::from_raw_value) + .or(token_info.chatgpt_plan_type) + .or(Some(PlanType::Unknown("unknown".to_string()))); + let tokens = TokenData { + id_token: token_info, + access_token: access_token.to_string(), + refresh_token: String::new(), + account_id: Some(chatgpt_account_id.to_string()), + }; + + Ok(Self { + auth_mode: Some(AuthMode::ChatgptAuthTokens), + openai_api_key: None, + tokens: Some(tokens), + last_refresh: Some(Utc::now()), + }) + } + + pub(super) fn resolved_mode(&self) -> AuthMode { + if let Some(mode) = self.auth_mode { + return mode; + } + if self.openai_api_key.is_some() { + return AuthMode::ApiKey; + } + AuthMode::Chatgpt + } + + pub(super) fn storage_mode( + &self, + auth_credentials_store_mode: AuthCredentialsStoreMode, + ) -> AuthCredentialsStoreMode { + if self.resolved_mode() == AuthMode::ChatgptAuthTokens { + AuthCredentialsStoreMode::Ephemeral + } else { + auth_credentials_store_mode + } + } +} + pub(super) fn get_auth_file(codex_home: &Path) -> PathBuf { codex_home.join("auth.json") }