Compare commits

...

12 Commits

Author SHA1 Message Date
Ahmed Ibrahim
5acc0ccfee Format auth client callsites
Co-authored-by: Codex <noreply@openai.com>
2026-03-19 10:14:56 -07:00
Ahmed Ibrahim
ff28d9cd08 Use core default client for auth loading
Keep auth behavior identical after the crate split by passing the existing core default client into production auth-loading paths instead of constructing a bare reqwest client in codex-auth.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 10:12:16 -07:00
Ahmed Ibrahim
c747662adc codex: fix CI failure on PR #15150
Construct AuthConfig explicitly at the remaining login restriction callsites after the auth split.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 09:45:12 -07:00
Ahmed Ibrahim
0c8b3309d8 codex: fix CI failure on PR #15150
Pass the extracted auth config to enforce_login_restrictions at the remaining Rust callsites.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 09:33:52 -07:00
Ahmed Ibrahim
1774364ee8 codex: fix CI failure on PR #15150
Point the extracted auth Bazel crate at its nonstandard root-level Rust sources so the :auth library target is generated.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 09:26:25 -07:00
Ahmed Ibrahim
ce4382c8c5 codex: fix CI failure on PR #15150
Declare the extracted auth package as a Bazel Rust crate target so codex-core can depend on it under Bazel.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 09:22:54 -07:00
Ahmed Ibrahim
da91e51f94 codex: fix CI failure on PR #15150
Define the auth source filegroup expected by the parent Bazel package after extracting auth into a nested crate.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 09:17:45 -07:00
Ahmed Ibrahim
2d4c8947f9 codex: fix CI failure on PR #15150
Add a Bazel package marker for the extracted auth crate so the new source package is visible to Bazel.

Co-authored-by: Codex <noreply@openai.com>
2026-03-19 09:12:39 -07:00
Ahmed Ibrahim
144a0fb312 fix 2026-03-19 00:04:27 -07:00
Ahmed Ibrahim
2c1d6d4e6a codex: fix CI failure on PR #15150
Make auth testing constructors public for codex-core callers across the new crate boundary.

Co-authored-by: Codex <noreply@openai.com>
2026-03-18 23:59:11 -07:00
Ahmed Ibrahim
1412ccd128 codex: fix CI failure on PR #15150
Fix moved auth test imports, add auth-crate test deps, and expose testing constructors used by codex-core.

Co-authored-by: Codex <noreply@openai.com>
2026-03-18 23:56:40 -07:00
Ahmed Ibrahim
87d9fe453a Make core auth a separate crate
Move auth-owned source and tests into core/src/auth as a standalone workspace crate, and keep codex-core consuming it via re-exports.

Co-authored-by: Codex <noreply@openai.com>
2026-03-18 23:46:01 -07:00
20 changed files with 262 additions and 81 deletions

29
codex-rs/Cargo.lock generated
View File

@@ -1596,6 +1596,34 @@ dependencies = [
"tokio-util",
]
[[package]]
name = "codex-auth"
version = "0.0.0"
dependencies = [
"anyhow",
"async-trait",
"base64 0.22.1",
"chrono",
"codex-app-server-protocol",
"codex-client",
"codex-keyring-store",
"codex-otel",
"codex-protocol",
"keyring",
"once_cell",
"pretty_assertions",
"reqwest",
"schemars 0.8.22",
"serde",
"serde_json",
"serial_test",
"sha2",
"tempfile",
"thiserror 2.0.18",
"tokio",
"tracing",
]
[[package]]
name = "codex-backend-client"
version = "0.0.0"
@@ -1840,6 +1868,7 @@ dependencies = [
"codex-arg0",
"codex-artifacts",
"codex-async-utils",
"codex-auth",
"codex-client",
"codex-config",
"codex-connectors",

View File

@@ -10,6 +10,7 @@ members = [
"debug-client",
"apply-patch",
"arg0",
"core/src/auth",
"feedback",
"codex-backend-openapi-models",
"cloud-requirements",
@@ -97,6 +98,7 @@ codex-app-server-test-client = { path = "app-server-test-client" }
codex-apply-patch = { path = "apply-patch" }
codex-arg0 = { path = "arg0" }
codex-async-utils = { path = "async-utils" }
codex-auth = { path = "core/src/auth" }
codex-backend-client = { path = "backend-client" }
codex-chatgpt = { path = "chatgpt" }
codex-cli = { path = "cli" }

View File

@@ -14,6 +14,7 @@ use codex_core::auth::CLIENT_ID;
use codex_core::auth::login_with_api_key;
use codex_core::auth::logout;
use codex_core::config::Config;
use codex_core::default_client::create_client;
use codex_login::ServerOptions;
use codex_login::run_device_code_login;
use codex_login::run_login_server;
@@ -316,7 +317,11 @@ pub async fn run_login_with_device_code_fallback_to_browser(
pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides).await;
match CodexAuth::from_auth_storage(&config.codex_home, config.cli_auth_credentials_store_mode) {
match CodexAuth::from_auth_storage_with_client(
&config.codex_home,
config.cli_auth_credentials_store_mode,
create_client(),
) {
Ok(Some(auth)) => match auth.auth_mode() {
AuthMode::ApiKey => match auth.get_token() {
Ok(api_key) => {

View File

@@ -31,6 +31,7 @@ codex-api = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-apply-patch = { workspace = true }
codex-async-utils = { workspace = true }
codex-auth = { workspace = true }
codex-client = { workspace = true }
codex-connectors = { workspace = true }
codex-config = { workspace = true }

View File

@@ -0,0 +1,7 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "auth",
crate_name = "codex_auth",
crate_srcs = glob(["*.rs"]),
)

View File

@@ -0,0 +1,39 @@
[package]
edition.workspace = true
license.workspace = true
name = "codex-auth"
version.workspace = true
[lib]
doctest = false
name = "codex_auth"
path = "lib.rs"
[lints]
workspace = true
[dependencies]
async-trait = { workspace = true }
base64 = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
codex-app-server-protocol = { workspace = true }
codex-client = { workspace = true }
codex-keyring-store = { workspace = true }
codex-otel = { workspace = true }
codex-protocol = { workspace = true }
once_cell = { workspace = true }
reqwest = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
anyhow = { workspace = true }
keyring = { workspace = true }
pretty_assertions = { workspace = true }
serial_test = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }

View File

@@ -1,5 +1,3 @@
mod storage;
use async_trait::async_trait;
use chrono::Utc;
use reqwest::StatusCode;
@@ -19,13 +17,12 @@ use codex_app_server_protocol::AuthMode as ApiAuthMode;
use codex_otel::TelemetryAuthMode;
use codex_protocol::config_types::ForcedLoginMethod;
pub use crate::auth::storage::AuthCredentialsStoreMode;
pub use crate::auth::storage::AuthDotJson;
use crate::auth::storage::AuthStorageBackend;
use crate::auth::storage::create_auth_storage;
use crate::config::Config;
use crate::error::RefreshTokenFailedError;
use crate::error::RefreshTokenFailedReason;
pub use crate::storage::AuthCredentialsStoreMode;
pub use crate::storage::AuthDotJson;
use crate::storage::AuthStorageBackend;
use crate::storage::create_auth_storage;
use crate::token_data::KnownPlan as InternalKnownPlan;
use crate::token_data::PlanType as InternalPlanType;
use crate::token_data::TokenData;
@@ -194,10 +191,23 @@ impl CodexAuth {
codex_home: &Path,
auth_credentials_store_mode: AuthCredentialsStoreMode,
) -> std::io::Result<Option<Self>> {
load_auth(
Self::from_auth_storage_with_client(
codex_home,
auth_credentials_store_mode,
create_client(),
)
}
pub fn from_auth_storage_with_client(
codex_home: &Path,
auth_credentials_store_mode: AuthCredentialsStoreMode,
client: CodexHttpClient,
) -> std::io::Result<Option<Self>> {
load_auth_with_client(
codex_home,
/*enable_codex_api_key_env*/ false,
auth_credentials_store_mode,
client,
)
}
@@ -335,7 +345,7 @@ impl CodexAuth {
last_refresh: Some(Utc::now()),
};
let client = crate::default_client::create_client();
let client = create_client();
let state = ChatgptAuthState {
auth_dot_json: Arc::new(Mutex::new(Some(auth_dot_json))),
client,
@@ -351,7 +361,7 @@ impl CodexAuth {
}
pub fn from_api_key(api_key: &str) -> Self {
Self::from_api_key_with_client(api_key, crate::default_client::create_client())
Self::from_api_key_with_client(api_key, create_client())
}
}
@@ -458,11 +468,27 @@ pub fn load_auth_dot_json(
storage.load()
}
pub fn enforce_login_restrictions(config: &Config) -> std::io::Result<()> {
let Some(auth) = load_auth(
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthConfig {
pub codex_home: PathBuf,
pub auth_credentials_store_mode: AuthCredentialsStoreMode,
pub forced_login_method: Option<ForcedLoginMethod>,
pub forced_chatgpt_workspace_id: Option<String>,
}
pub fn enforce_login_restrictions(config: &AuthConfig) -> std::io::Result<()> {
enforce_login_restrictions_with_client(config, create_client())
}
pub fn enforce_login_restrictions_with_client(
config: &AuthConfig,
client: CodexHttpClient,
) -> std::io::Result<()> {
let Some(auth) = load_auth_with_client(
&config.codex_home,
/*enable_codex_api_key_env*/ true,
config.cli_auth_credentials_store_mode,
config.auth_credentials_store_mode,
client,
)?
else {
return Ok(());
@@ -486,7 +512,7 @@ pub fn enforce_login_restrictions(config: &Config) -> std::io::Result<()> {
return logout_with_message(
&config.codex_home,
message,
config.cli_auth_credentials_store_mode,
config.auth_credentials_store_mode,
);
}
}
@@ -504,7 +530,7 @@ pub fn enforce_login_restrictions(config: &Config) -> std::io::Result<()> {
format!(
"Failed to load ChatGPT credentials while enforcing workspace restrictions: {err}. Logging out."
),
config.cli_auth_credentials_store_mode,
config.auth_credentials_store_mode,
);
}
};
@@ -523,7 +549,7 @@ pub fn enforce_login_restrictions(config: &Config) -> std::io::Result<()> {
return logout_with_message(
&config.codex_home,
message,
config.cli_auth_credentials_store_mode,
config.auth_credentials_store_mode,
);
}
}
@@ -562,18 +588,30 @@ fn load_auth(
codex_home: &Path,
enable_codex_api_key_env: bool,
auth_credentials_store_mode: AuthCredentialsStoreMode,
) -> std::io::Result<Option<CodexAuth>> {
load_auth_with_client(
codex_home,
enable_codex_api_key_env,
auth_credentials_store_mode,
create_client(),
)
}
fn load_auth_with_client(
codex_home: &Path,
enable_codex_api_key_env: bool,
auth_credentials_store_mode: AuthCredentialsStoreMode,
client: CodexHttpClient,
) -> std::io::Result<Option<CodexAuth>> {
let build_auth = |auth_dot_json: AuthDotJson, storage_mode| {
let client = crate::default_client::create_client();
CodexAuth::from_auth_dot_json(codex_home, auth_dot_json, storage_mode, client)
CodexAuth::from_auth_dot_json(codex_home, auth_dot_json, storage_mode, client.clone())
};
// API key via env var takes precedence over any other auth method.
if enable_codex_api_key_env && let Some(api_key) = read_codex_api_key_from_env() {
let client = crate::default_client::create_client();
return Ok(Some(CodexAuth::from_api_key_with_client(
api_key.as_str(),
client,
client.clone(),
)));
}
@@ -1077,7 +1115,7 @@ impl AuthManager {
}
/// Create an AuthManager with a specific CodexAuth, for testing only.
pub(crate) fn from_auth_for_testing(auth: CodexAuth) -> Arc<Self> {
pub fn from_auth_for_testing(auth: CodexAuth) -> Arc<Self> {
let cached = CachedAuth {
auth: Some(auth),
external_refresher: None,
@@ -1093,10 +1131,7 @@ impl AuthManager {
}
/// Create an AuthManager with a specific CodexAuth and codex home, for testing only.
pub(crate) fn from_auth_for_testing_with_home(
auth: CodexAuth,
codex_home: PathBuf,
) -> Arc<Self> {
pub fn from_auth_for_testing_with_home(auth: CodexAuth, codex_home: PathBuf) -> Arc<Self> {
let cached = CachedAuth {
auth: Some(auth),
external_refresher: None,
@@ -1449,3 +1484,7 @@ impl AuthManager {
#[cfg(test)]
#[path = "auth_tests.rs"]
mod tests;
fn create_client() -> CodexHttpClient {
CodexHttpClient::new(reqwest::Client::new())
}

View File

@@ -1,8 +1,6 @@
use super::*;
use crate::auth::storage::FileAuthStorage;
use crate::auth::storage::get_auth_file;
use crate::config::Config;
use crate::config::ConfigBuilder;
use crate::storage::FileAuthStorage;
use crate::storage::get_auth_file;
use crate::token_data::IdTokenInfo;
use crate::token_data::KnownPlan as InternalKnownPlan;
use crate::token_data::PlanType as InternalPlanType;
@@ -260,15 +258,13 @@ async fn build_config(
codex_home: &Path,
forced_login_method: Option<ForcedLoginMethod>,
forced_chatgpt_workspace_id: Option<String>,
) -> Config {
let mut config = ConfigBuilder::default()
.codex_home(codex_home.to_path_buf())
.build()
.await
.expect("config should load");
config.forced_login_method = forced_login_method;
config.forced_chatgpt_workspace_id = forced_chatgpt_workspace_id;
config
) -> AuthConfig {
AuthConfig {
codex_home: codex_home.to_path_buf(),
auth_credentials_store_mode: AuthCredentialsStoreMode::File,
forced_login_method,
forced_chatgpt_workspace_id,
}
}
/// Use sparingly.

View File

@@ -0,0 +1,25 @@
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{message}")]
pub struct RefreshTokenFailedError {
pub reason: RefreshTokenFailedReason,
pub message: String,
}
impl RefreshTokenFailedError {
pub fn new(reason: RefreshTokenFailedReason, message: impl Into<String>) -> Self {
Self {
reason,
message: message.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RefreshTokenFailedReason {
Expired,
Exhausted,
Revoked,
Other,
}

View File

@@ -0,0 +1,10 @@
pub mod error;
mod storage;
pub mod token_data;
mod util;
mod auth;
pub use auth::*;
pub use error::RefreshTokenFailedError;
pub use error::RefreshTokenFailedReason;

View File

@@ -27,7 +27,7 @@ pub struct IdTokenInfo {
/// The ChatGPT subscription plan type
/// (e.g., "free", "plus", "pro", "business", "enterprise", "edu").
/// (Note: values may vary by backend.)
pub(crate) chatgpt_plan_type: Option<PlanType>,
pub chatgpt_plan_type: Option<PlanType>,
/// ChatGPT user identifier associated with the token, if present.
pub chatgpt_user_id: Option<String>,
/// Organization/workspace identifier associated with the token, if present.
@@ -55,13 +55,13 @@ impl IdTokenInfo {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum PlanType {
pub enum PlanType {
Known(KnownPlan),
Unknown(String),
}
impl PlanType {
pub(crate) fn from_raw_value(raw: &str) -> Self {
pub fn from_raw_value(raw: &str) -> Self {
match raw.to_ascii_lowercase().as_str() {
"free" => Self::Known(KnownPlan::Free),
"go" => Self::Known(KnownPlan::Go),
@@ -78,7 +78,7 @@ impl PlanType {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum KnownPlan {
pub enum KnownPlan {
Free,
Go,
Plus,

View File

@@ -0,0 +1,16 @@
use tracing::debug;
pub(crate) fn try_parse_error_message(text: &str) -> String {
debug!("Parsing server error response: {}", text);
let json = serde_json::from_str::<serde_json::Value>(text).unwrap_or_default();
if let Some(error) = json.get("error")
&& let Some(message) = error.get("message")
&& let Some(message_str) = message.as_str()
{
return message_str.to_string();
}
if text.is_empty() {
return "Unknown error".to_string();
}
text.to_string()
}

View File

@@ -9,6 +9,8 @@ use chrono::Datelike;
use chrono::Local;
use chrono::Utc;
use codex_async_utils::CancelErr;
pub use codex_auth::RefreshTokenFailedError;
pub use codex_auth::RefreshTokenFailedReason;
use codex_protocol::ThreadId;
use codex_protocol::protocol::CodexErrorInfo;
use codex_protocol::protocol::ErrorEvent;
@@ -261,30 +263,6 @@ impl std::fmt::Display for ResponseStreamFailed {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{message}")]
pub struct RefreshTokenFailedError {
pub reason: RefreshTokenFailedReason,
pub message: String,
}
impl RefreshTokenFailedError {
pub fn new(reason: RefreshTokenFailedReason, message: impl Into<String>) -> Self {
Self {
reason,
message: message.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RefreshTokenFailedReason {
Expired,
Exhausted,
Revoked,
Other,
}
#[derive(Debug)]
pub struct UnexpectedResponseError {
pub status: StatusCode,

View File

@@ -10,7 +10,7 @@ pub mod api_bridge;
mod apply_patch;
mod apps;
mod arc_monitor;
pub mod auth;
pub use codex_auth as auth;
mod auth_env_telemetry;
mod client;
mod client_common;
@@ -76,7 +76,7 @@ mod shell_detect;
mod stream_events_utils;
pub mod test_support;
mod text_encoding;
pub mod token_data;
pub use codex_auth::token_data;
mod truncate;
mod unified_exec;
pub mod windows_sandbox;

View File

@@ -45,7 +45,8 @@ use codex_cloud_requirements::cloud_requirements_loader;
use codex_core::AuthManager;
use codex_core::LMSTUDIO_OSS_PROVIDER_ID;
use codex_core::OLLAMA_OSS_PROVIDER_ID;
use codex_core::auth::enforce_login_restrictions;
use codex_core::auth::AuthConfig;
use codex_core::auth::enforce_login_restrictions_with_client;
use codex_core::check_execpolicy_for_warnings;
use codex_core::config::Config;
use codex_core::config::ConfigBuilder;
@@ -56,6 +57,7 @@ use codex_core::config::resolve_oss_provider;
use codex_core::config_loader::ConfigLoadError;
use codex_core::config_loader::LoaderOverrides;
use codex_core::config_loader::format_config_error_with_source;
use codex_core::default_client::create_client;
use codex_core::format_exec_policy_error_with_source;
use codex_core::git_info::get_git_repo_root;
use codex_feedback::CodexFeedback;
@@ -381,7 +383,15 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
set_default_client_residency_requirement(config.enforce_residency.value());
if let Err(err) = enforce_login_restrictions(&config) {
if let Err(err) = enforce_login_restrictions_with_client(
&AuthConfig {
codex_home: config.codex_home.clone(),
auth_credentials_store_mode: config.cli_auth_credentials_store_mode,
forced_login_method: config.forced_login_method,
forced_chatgpt_workspace_id: config.forced_chatgpt_workspace_id.clone(),
},
create_client(),
) {
eprintln!("{err}");
std::process::exit(1);
}

View File

@@ -14,7 +14,6 @@ use codex_core::INTERACTIVE_SESSION_SOURCES;
use codex_core::RolloutRecorder;
use codex_core::ThreadSortKey;
use codex_core::auth::AuthMode;
use codex_core::auth::enforce_login_restrictions;
use codex_core::check_execpolicy_for_warnings;
use codex_core::config::Config;
use codex_core::config::ConfigBuilder;
@@ -26,6 +25,7 @@ use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::ConfigLoadError;
use codex_core::config_loader::LoaderOverrides;
use codex_core::config_loader::format_config_error_with_source;
use codex_core::default_client::create_client;
use codex_core::default_client::set_default_client_residency_requirement;
use codex_core::find_thread_path_by_id_str;
use codex_core::find_thread_path_by_name_str;
@@ -1155,7 +1155,11 @@ fn get_login_status(config: &Config) -> LoginStatus {
// Reading the OpenAI API key is an async operation because it may need
// to refresh the token. Block on it.
let codex_home = config.codex_home.clone();
match CodexAuth::from_auth_storage(&codex_home, config.cli_auth_credentials_store_mode) {
match CodexAuth::from_auth_storage_with_client(
&codex_home,
config.cli_auth_credentials_store_mode,
create_client(),
) {
Ok(Some(auth)) => LoginStatus::AuthMode(auth.auth_mode()),
Ok(None) => LoginStatus::NotAuthenticated,
Err(err) => {

View File

@@ -7,6 +7,7 @@ use codex_client::build_reqwest_client_with_custom_ca;
use codex_core::auth::AuthCredentialsStoreMode;
use codex_core::config::Config;
use codex_core::config::find_codex_home;
use codex_core::default_client::create_client;
use codex_core::default_client::get_codex_user_agent;
use codex_login::AuthMode;
use codex_login::CodexAuth;
@@ -926,9 +927,13 @@ fn normalize_chatgpt_base_url(input: &str) -> String {
async fn resolve_auth() -> Result<TranscriptionAuthContext, String> {
let codex_home = find_codex_home().map_err(|e| format!("failed to find codex home: {e}"))?;
let auth = CodexAuth::from_auth_storage(&codex_home, AuthCredentialsStoreMode::Auto)
.map_err(|e| format!("failed to read auth.json: {e}"))?
.ok_or_else(|| "No Codex auth is configured; please run `codex login`".to_string())?;
let auth = CodexAuth::from_auth_storage_with_client(
&codex_home,
AuthCredentialsStoreMode::Auto,
create_client(),
)
.map_err(|e| format!("failed to read auth.json: {e}"))?
.ok_or_else(|| "No Codex auth is configured; please run `codex login`".to_string())?;
let chatgpt_account_id = auth.get_account_id();

View File

@@ -21,7 +21,8 @@ use codex_app_server_protocol::ThreadListParams;
use codex_app_server_protocol::ThreadSortKey as AppServerThreadSortKey;
use codex_app_server_protocol::ThreadSourceKind;
use codex_cloud_requirements::cloud_requirements_loader_for_storage;
use codex_core::auth::enforce_login_restrictions;
use codex_core::auth::AuthConfig;
use codex_core::auth::enforce_login_restrictions_with_client;
use codex_core::check_execpolicy_for_warnings;
use codex_core::config::Config;
use codex_core::config::ConfigBuilder;
@@ -33,6 +34,7 @@ use codex_core::config_loader::CloudRequirementsLoader;
use codex_core::config_loader::ConfigLoadError;
use codex_core::config_loader::LoaderOverrides;
use codex_core::config_loader::format_config_error_with_source;
use codex_core::default_client::create_client;
use codex_core::default_client::set_default_client_residency_requirement;
use codex_core::format_exec_policy_error_with_source;
use codex_core::path_utils;
@@ -776,7 +778,15 @@ pub async fn run_main(
if matches!(app_server_target, AppServerTarget::Embedded) {
#[allow(clippy::print_stderr)]
if let Err(err) = enforce_login_restrictions(&config) {
if let Err(err) = enforce_login_restrictions_with_client(
&AuthConfig {
codex_home: config.codex_home.clone(),
auth_credentials_store_mode: config.cli_auth_credentials_store_mode,
forced_login_method: config.forced_login_method,
forced_chatgpt_workspace_id: config.forced_chatgpt_workspace_id.clone(),
},
create_client(),
) {
eprintln!("{err}");
std::process::exit(1);
}

View File

@@ -5,6 +5,7 @@ use codex_client::build_reqwest_client_with_custom_ca;
use codex_core::auth::AuthCredentialsStoreMode;
use codex_core::config::Config;
use codex_core::config::find_codex_home;
use codex_core::default_client::create_client;
use codex_core::default_client::get_codex_user_agent;
use codex_login::AuthMode;
use codex_login::CodexAuth;
@@ -764,9 +765,13 @@ fn normalize_chatgpt_base_url(input: &str) -> String {
async fn resolve_auth() -> Result<TranscriptionAuthContext, String> {
let codex_home = find_codex_home().map_err(|e| format!("failed to find codex home: {e}"))?;
let auth = CodexAuth::from_auth_storage(&codex_home, AuthCredentialsStoreMode::Auto)
.map_err(|e| format!("failed to read auth.json: {e}"))?
.ok_or_else(|| "No Codex auth is configured; please run `codex login`".to_string())?;
let auth = CodexAuth::from_auth_storage_with_client(
&codex_home,
AuthCredentialsStoreMode::Auto,
create_client(),
)
.map_err(|e| format!("failed to read auth.json: {e}"))?
.ok_or_else(|| "No Codex auth is configured; please run `codex login`".to_string())?;
let chatgpt_account_id = auth.get_account_id();