feat: refactor CodexAuth so invalid state cannot be represented (#10208)

Previously, `CodexAuth` was defined as follows:


d550fbf41a/codex-rs/core/src/auth.rs (L39-L46)

But if you looked at its constructors, we had creation for
`AuthMode::ApiKey` where `storage` was built using a nonsensical path
(`PathBuf::new()`) and `auth_dot_json` was `None`:


d550fbf41a/codex-rs/core/src/auth.rs (L212-L220)

By comparison, when `AuthMode::ChatGPT` was used, `api_key` was always
`None`:


d550fbf41a/codex-rs/core/src/auth.rs (L665-L671)

https://github.com/openai/codex/pull/10012 took things further because
it introduced a new `ChatgptAuthTokens` variant to `AuthMode`, which is
important in when invoking `account/login/start` via the app server, but
most logic _internal_ to the app server should just reason about two
`AuthMode` variants: `ApiKey` and `ChatGPT`.

This PR tries to clean things up as follows:

- `LoginAccountParams` and `AuthMode` in `codex-rs/app-server-protocol/`
both continue to have the `ChatgptAuthTokens` variant, though it is used
exclusively for the on-the-wire messaging.
- `codex-rs/core/src/auth.rs` now has its own `AuthMode` enum, which
only has two variants: `ApiKey` and `ChatGPT`.
- `CodexAuth` has been changed from a struct to an enum. It is a
disjoint union where each variant (`ApiKey`, `ChatGpt`, and
`ChatGptAuthTokens`) have only the associated fields that make sense for
that variant.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/10208).
* #10224
* __->__ #10208
This commit is contained in:
Michael Bolin
2026-01-30 09:33:23 -08:00
committed by GitHub
parent 0212f4010e
commit 377ab0c77c
13 changed files with 286 additions and 165 deletions

View File

@@ -27,7 +27,6 @@ use codex_api::common::ResponsesWsRequest;
use codex_api::create_text_param_for_request;
use codex_api::error::ApiError;
use codex_api::requests::responses::Compression;
use codex_app_server_protocol::AuthMode;
use codex_otel::OtelManager;
use codex_protocol::ThreadId;
@@ -50,6 +49,7 @@ use tokio::sync::mpsc;
use tracing::warn;
use crate::AuthManager;
use crate::auth::CodexAuth;
use crate::auth::RefreshTokenError;
use crate::client_common::Prompt;
use crate::client_common::ResponseEvent;
@@ -220,7 +220,7 @@ impl ModelClient {
let api_provider = self
.state
.provider
.to_api_provider(auth.as_ref().map(|a| a.mode))?;
.to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?;
let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?;
let transport = ReqwestTransport::new(build_reqwest_client());
let request_telemetry = self.build_request_telemetry();
@@ -469,9 +469,7 @@ impl ModelClientSession {
.config
.features
.enabled(Feature::EnableRequestCompression)
&& auth.is_some_and(|auth| {
matches!(auth.mode, AuthMode::ChatGPT | AuthMode::ChatgptAuthTokens)
})
&& auth.is_some_and(CodexAuth::is_chatgpt_auth)
&& self.state.provider.is_openai()
{
Compression::Zstd
@@ -509,7 +507,7 @@ impl ModelClientSession {
let api_provider = self
.state
.provider
.to_api_provider(auth.as_ref().map(|a| a.mode))?;
.to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?;
let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?;
let transport = ReqwestTransport::new(build_reqwest_client());
let (request_telemetry, sse_telemetry) = self.build_streaming_telemetry();
@@ -565,7 +563,7 @@ impl ModelClientSession {
let api_provider = self
.state
.provider
.to_api_provider(auth.as_ref().map(|a| a.mode))?;
.to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?;
let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?;
let transport = ReqwestTransport::new(build_reqwest_client());
let (request_telemetry, sse_telemetry) = self.build_streaming_telemetry();
@@ -611,7 +609,7 @@ impl ModelClientSession {
let api_provider = self
.state
.provider
.to_api_provider(auth.as_ref().map(|a| a.mode))?;
.to_api_provider(auth.as_ref().map(CodexAuth::internal_auth_mode))?;
let api_auth = auth_provider_from_auth(auth.clone(), &self.state.provider)?;
let compression = self.responses_request_compression(auth.as_ref());