auth: move domain mode below app wire types (#29721)

## Why

Authentication mode is a domain concept used by login, model selection,
telemetry, and transports. Keeping the canonical type in app-server
protocol forces those lower-level crates to depend on an unrelated wire
API.

## What changed

- Added canonical `codex_protocol::auth::AuthMode` domain values.
- Kept the app-server wire DTO unchanged and added an explicit app-side
conversion.
- Removed production app-server-protocol dependencies from login,
model-provider-info, models-manager, and otel call paths.

## Stack

This is PR 2 of 6, stacked on [PR
#29714](https://github.com/openai/codex/pull/29714). Review only the
delta from `codex/split-json-rpc-protocols`. Next: [PR
#29722](https://github.com/openai/codex/pull/29722).

## Validation

- Auth and login coverage passed in the focused protocol/domain test
run.
- App-server account and auth conversion coverage passed.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-23 20:10:20 -07:00
committed by GitHub
Unverified
parent 806a4b66c9
commit 31372078d1
47 changed files with 224 additions and 146 deletions
+1 -4
View File
@@ -2163,6 +2163,7 @@ dependencies = [
"codex-core",
"codex-login",
"codex-model-provider",
"codex-protocol",
"codex-state",
"codex-uds",
"codex-utils-absolute-path",
@@ -3263,7 +3264,6 @@ dependencies = [
"base64 0.22.1",
"chrono",
"codex-agent-identity",
"codex-app-server-protocol",
"codex-client",
"codex-config",
"codex-keyring-store",
@@ -3498,7 +3498,6 @@ name = "codex-model-provider-info"
version = "0.0.0"
dependencies = [
"codex-api",
"codex-app-server-protocol",
"codex-protocol",
"codex-utils-absolute-path",
"http 1.4.0",
@@ -3515,7 +3514,6 @@ name = "codex-models-manager"
version = "0.0.0"
dependencies = [
"chrono",
"codex-app-server-protocol",
"codex-collaboration-mode-templates",
"codex-login",
"codex-otel",
@@ -3591,7 +3589,6 @@ version = "0.0.0"
dependencies = [
"chrono",
"codex-api",
"codex-app-server-protocol",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-string",
+1
View File
@@ -55,6 +55,7 @@ uuid = { workspace = true, features = ["serde", "v7"] }
[dev-dependencies]
chrono = { workspace = true }
codex-config = { workspace = true }
codex-protocol = { workspace = true }
pretty_assertions = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = ["test-util"] }
@@ -18,7 +18,6 @@ use crate::transport::CHANNEL_CAPACITY;
use crate::transport::ConnectionOrigin;
use crate::transport::TransportEvent;
use base64::Engine;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::ConfigWarningNotification;
use codex_app_server_protocol::JSONRPCMessage;
use codex_app_server_protocol::RemoteControlConnectionStatus;
@@ -36,6 +35,7 @@ use codex_login::CodexAuth;
use codex_login::save_auth;
use codex_login::token_data::TokenData;
use codex_login::token_data::parse_chatgpt_jwt_claims;
use codex_protocol::auth::AuthMode;
use codex_state::RemoteControlEnrollmentRecord;
use codex_state::StateRuntime;
use futures::SinkExt;
@@ -1813,7 +1813,6 @@ mod tests {
use crate::transport::remote_control::protocol::StreamId;
use crate::transport::remote_control::protocol::normalize_remote_control_url;
use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::ConfigWarningNotification;
use codex_app_server_protocol::JSONRPCMessage;
use codex_app_server_protocol::JSONRPCNotification;
@@ -1826,6 +1825,7 @@ mod tests {
use codex_login::save_auth;
use codex_login::token_data::TokenData;
use codex_login::token_data::parse_chatgpt_jwt_claims;
use codex_protocol::auth::AuthMode;
use codex_state::StateRuntime;
use futures::StreamExt;
use pretty_assertions::assert_eq;
+19
View File
@@ -0,0 +1,19 @@
use codex_app_server_protocol::AuthMode as ApiAuthMode;
use codex_protocol::auth::AuthMode;
/// Converts the domain auth mode owned by `codex-protocol` into the app-server wire type owned by
/// `codex-app-server-protocol`.
///
/// The types stay separate so app-server protocol ownership does not leak into domain crates.
/// Because this crate owns neither type, Rust's orphan rules require an explicit conversion
/// function instead of a `From` implementation.
pub(crate) fn auth_mode_to_api(auth_mode: AuthMode) -> ApiAuthMode {
match auth_mode {
AuthMode::ApiKey => ApiAuthMode::ApiKey,
AuthMode::Chatgpt => ApiAuthMode::Chatgpt,
AuthMode::ChatgptAuthTokens => ApiAuthMode::ChatgptAuthTokens,
AuthMode::AgentIdentity => ApiAuthMode::AgentIdentity,
AuthMode::PersonalAccessToken => ApiAuthMode::PersonalAccessToken,
AuthMode::BedrockApiKey => ApiAuthMode::BedrockApiKey,
}
}
+1
View File
@@ -82,6 +82,7 @@ const SQLITE_RECOVERY_CONFIG_WARNING_SUMMARY: &str = "Codex rebuilt its local da
mod analytics_utils;
mod app_server_tracing;
mod attestation;
mod auth_mode;
mod bespoke_event_handling;
mod command_exec;
mod config;
+1 -1
View File
@@ -50,7 +50,6 @@ use crate::transport::AppServerTransport;
use crate::transport::RemoteControlHandle;
use codex_analytics::AnalyticsEventsClient;
use codex_analytics::AppServerRpcTransport;
use codex_app_server_protocol::AuthMode as LoginAuthMode;
use codex_app_server_protocol::ChatgptAuthTokensRefreshParams;
use codex_app_server_protocol::ChatgptAuthTokensRefreshReason;
use codex_app_server_protocol::ChatgptAuthTokensRefreshResponse;
@@ -80,6 +79,7 @@ use codex_login::auth::ExternalAuthRefreshContext;
use codex_login::auth::ExternalAuthRefreshReason;
use codex_login::auth::ExternalAuthTokens;
use codex_protocol::ThreadId;
use codex_protocol::auth::AuthMode as LoginAuthMode;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::W3cTraceContext;
use codex_rollout::StateDbHandle;
@@ -1,4 +1,5 @@
use super::*;
use crate::auth_mode::auth_mode_to_api;
use chrono::DateTime;
mod rate_limit_resets;
@@ -179,7 +180,10 @@ impl AccountRequestProcessor {
fn current_account_updated_notification(&self) -> AccountUpdatedNotification {
let auth = self.auth_manager.auth_cached();
AccountUpdatedNotification {
auth_mode: auth.as_ref().map(CodexAuth::api_auth_mode),
auth_mode: auth
.as_ref()
.map(CodexAuth::api_auth_mode)
.map(auth_mode_to_api),
plan_type: auth.as_ref().and_then(CodexAuth::account_plan_type),
}
}
@@ -693,7 +697,10 @@ impl AccountRequestProcessor {
)
.await;
let payload_v2 = AccountUpdatedNotification {
auth_mode: auth.as_ref().map(CodexAuth::api_auth_mode),
auth_mode: auth
.as_ref()
.map(CodexAuth::api_auth_mode)
.map(auth_mode_to_api),
plan_type: auth.as_ref().and_then(CodexAuth::account_plan_type),
};
outgoing
@@ -730,7 +737,8 @@ impl AccountRequestProcessor {
.auth_manager
.auth_cached()
.as_ref()
.map(CodexAuth::api_auth_mode))
.map(CodexAuth::api_auth_mode)
.map(auth_mode_to_api))
}
async fn logout_v2(&self, request_id: ConnectionRequestId) -> Result<(), JSONRPCErrorError> {
@@ -801,7 +809,7 @@ impl AccountRequestProcessor {
Some(auth) => {
let permanent_refresh_failure =
self.auth_manager.refresh_failure_for_auth(&auth).is_some();
let auth_mode = auth.api_auth_mode();
let auth_mode = auth_mode_to_api(auth.api_auth_mode());
let (reported_auth_method, token_opt) = if matches!(
auth,
CodexAuth::AgentIdentity(_) | CodexAuth::PersonalAccessToken(_)
@@ -24,6 +24,7 @@ use codex_mcp::oauth_login_support;
use codex_mcp::should_retry_without_scopes;
use codex_plugin::PluginId;
use codex_plugin::PluginTelemetryMetadata;
use codex_protocol::auth::AuthMode as DomainAuthMode;
use codex_rmcp_client::perform_oauth_login_silent;
#[derive(Clone)]
@@ -567,7 +568,7 @@ impl PluginRequestProcessor {
let include_global_remote =
!explicit_marketplace_kinds && config.features.enabled(Feature::RemotePlugin);
let use_remote_global_catalog =
include_global_remote && auth_mode.is_some_and(AuthMode::uses_codex_backend);
include_global_remote && auth_mode.is_some_and(DomainAuthMode::uses_codex_backend);
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
@@ -6,13 +6,13 @@ use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use chrono::DateTime;
use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
use codex_login::save_auth;
use codex_login::token_data::TokenData;
use codex_login::token_data::parse_chatgpt_jwt_claims;
use codex_protocol::auth::AuthMode;
use serde_json::json;
/// Builder for writing a fake ChatGPT auth.json in tests.
@@ -27,7 +27,6 @@ use codex_app_server_protocol::AppReview;
use codex_app_server_protocol::AppScreenshot;
use codex_app_server_protocol::AppsListParams;
use codex_app_server_protocol::AppsListResponse;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::JSONRPCError;
use codex_app_server_protocol::JSONRPCResponse;
use codex_app_server_protocol::RequestId;
@@ -38,6 +37,7 @@ use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
use codex_login::save_auth;
use codex_protocol::auth::AuthMode;
use pretty_assertions::assert_eq;
use rmcp::handler::server::ServerHandler;
use rmcp::model::JsonObject;
+32 -34
View File
@@ -53,6 +53,7 @@ use codex_login::default_client::build_reqwest_client;
use codex_login::default_client::default_headers;
use codex_login::load_auth_dot_json;
use codex_model_provider::create_model_provider;
use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::AskForApproval;
use codex_terminal_detection::Multiplexer;
use codex_terminal_detection::TerminalInfo;
@@ -1323,27 +1324,27 @@ fn provider_specific_auth_check(
fn stored_auth_mode(auth: &codex_login::AuthDotJson) -> &'static str {
match stored_auth_mode_value(auth) {
codex_app_server_protocol::AuthMode::ApiKey => "api_key",
codex_app_server_protocol::AuthMode::Chatgpt => "chatgpt",
codex_app_server_protocol::AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens",
codex_app_server_protocol::AuthMode::AgentIdentity => "agent_identity",
codex_app_server_protocol::AuthMode::PersonalAccessToken => "personal_access_token",
codex_app_server_protocol::AuthMode::BedrockApiKey => "bedrock_api_key",
AuthMode::ApiKey => "api_key",
AuthMode::Chatgpt => "chatgpt",
AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens",
AuthMode::AgentIdentity => "agent_identity",
AuthMode::PersonalAccessToken => "personal_access_token",
AuthMode::BedrockApiKey => "bedrock_api_key",
}
}
fn stored_auth_mode_value(auth: &AuthDotJson) -> codex_app_server_protocol::AuthMode {
fn stored_auth_mode_value(auth: &AuthDotJson) -> AuthMode {
if let Some(mode) = auth.auth_mode {
return mode;
}
if auth.personal_access_token.is_some() {
codex_app_server_protocol::AuthMode::PersonalAccessToken
AuthMode::PersonalAccessToken
} else if auth.bedrock_api_key.is_some() {
codex_app_server_protocol::AuthMode::BedrockApiKey
AuthMode::BedrockApiKey
} else if auth.openai_api_key.is_some() {
codex_app_server_protocol::AuthMode::ApiKey
AuthMode::ApiKey
} else {
codex_app_server_protocol::AuthMode::Chatgpt
AuthMode::Chatgpt
}
}
@@ -1353,7 +1354,7 @@ fn stored_auth_issues(
) -> Vec<&'static str> {
let mut issues = Vec::new();
match stored_auth_mode_value(auth) {
codex_app_server_protocol::AuthMode::ApiKey => {
AuthMode::ApiKey => {
let stored_key_present = auth
.openai_api_key
.as_deref()
@@ -1364,7 +1365,7 @@ fn stored_auth_issues(
issues.push("API key auth is missing an API key");
}
}
codex_app_server_protocol::AuthMode::Chatgpt => {
AuthMode::Chatgpt => {
match auth.tokens.as_ref() {
Some(tokens) => {
if tokens.access_token.trim().is_empty() {
@@ -1380,7 +1381,7 @@ fn stored_auth_issues(
issues.push("ChatGPT auth is missing refresh metadata");
}
}
codex_app_server_protocol::AuthMode::ChatgptAuthTokens => {
AuthMode::ChatgptAuthTokens => {
match auth.tokens.as_ref() {
Some(tokens) => {
if tokens.access_token.trim().is_empty() {
@@ -1396,7 +1397,7 @@ fn stored_auth_issues(
issues.push("external ChatGPT auth is missing refresh metadata");
}
}
codex_app_server_protocol::AuthMode::AgentIdentity => {
AuthMode::AgentIdentity => {
if auth
.agent_identity
.as_ref()
@@ -1405,7 +1406,7 @@ fn stored_auth_issues(
issues.push("agent identity auth is missing an agent identity token");
}
}
codex_app_server_protocol::AuthMode::PersonalAccessToken => {
AuthMode::PersonalAccessToken => {
if auth
.personal_access_token
.as_deref()
@@ -1414,7 +1415,7 @@ fn stored_auth_issues(
issues.push("personal access token auth is missing a personal access token");
}
}
codex_app_server_protocol::AuthMode::BedrockApiKey => {
AuthMode::BedrockApiKey => {
if auth.bedrock_api_key.is_none() {
issues.push("Bedrock API key auth is missing a Bedrock API key");
}
@@ -2462,12 +2463,12 @@ fn websocket_error_detail(err: &ApiError) -> String {
fn auth_mode_name(auth: &CodexAuth) -> &'static str {
match auth.auth_mode() {
codex_app_server_protocol::AuthMode::ApiKey => "api_key",
codex_app_server_protocol::AuthMode::Chatgpt => "chatgpt",
codex_app_server_protocol::AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens",
codex_app_server_protocol::AuthMode::AgentIdentity => "agent_identity",
codex_app_server_protocol::AuthMode::PersonalAccessToken => "personal_access_token",
codex_app_server_protocol::AuthMode::BedrockApiKey => "bedrock_api_key",
AuthMode::ApiKey => "api_key",
AuthMode::Chatgpt => "chatgpt",
AuthMode::ChatgptAuthTokens => "chatgpt_auth_tokens",
AuthMode::AgentIdentity => "agent_identity",
AuthMode::PersonalAccessToken => "personal_access_token",
AuthMode::BedrockApiKey => "bedrock_api_key",
}
}
@@ -2600,15 +2601,12 @@ fn provider_auth_reachability_mode_from_auth(
return ProviderAuthReachabilityMode::Chatgpt;
}
match stored_auth.map(stored_auth_mode_value) {
Some(AuthMode::ApiKey | AuthMode::BedrockApiKey) => ProviderAuthReachabilityMode::ApiKey,
Some(
codex_app_server_protocol::AuthMode::ApiKey
| codex_app_server_protocol::AuthMode::BedrockApiKey,
) => ProviderAuthReachabilityMode::ApiKey,
Some(
codex_app_server_protocol::AuthMode::Chatgpt
| codex_app_server_protocol::AuthMode::ChatgptAuthTokens
| codex_app_server_protocol::AuthMode::AgentIdentity
| codex_app_server_protocol::AuthMode::PersonalAccessToken,
AuthMode::Chatgpt
| AuthMode::ChatgptAuthTokens
| AuthMode::AgentIdentity
| AuthMode::PersonalAccessToken,
)
| None => ProviderAuthReachabilityMode::Chatgpt,
}
@@ -3502,7 +3500,7 @@ mod tests {
#[test]
fn stored_auth_validation_rejects_missing_api_key() {
let auth = AuthDotJson {
auth_mode: Some(codex_app_server_protocol::AuthMode::ApiKey),
auth_mode: Some(AuthMode::ApiKey),
openai_api_key: None,
tokens: None,
last_refresh: None,
@@ -3554,7 +3552,7 @@ mod tests {
assert_eq!(stored_auth_mode(&auth), "personal_access_token");
assert!(stored_auth_issues(&auth, |_| false).is_empty());
auth.auth_mode = Some(codex_app_server_protocol::AuthMode::PersonalAccessToken);
auth.auth_mode = Some(AuthMode::PersonalAccessToken);
auth.personal_access_token = None;
assert_eq!(
stored_auth_issues(&auth, |_| false),
@@ -3565,7 +3563,7 @@ mod tests {
#[test]
fn provider_reachability_mode_uses_api_key_auth() {
let api_key_auth = AuthDotJson {
auth_mode: Some(codex_app_server_protocol::AuthMode::ApiKey),
auth_mode: Some(AuthMode::ApiKey),
openai_api_key: Some("sk-test".to_string()),
tokens: None,
last_refresh: None,
+1 -1
View File
@@ -7,7 +7,6 @@
//! into a one-shot CLI command while still producing a durable `codex-login.log` artifact that
//! support can request from users.
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_core::config::Config;
use codex_login::AuthKeyringBackendKind;
@@ -20,6 +19,7 @@ use codex_login::login_with_api_key;
use codex_login::logout_with_revoke;
use codex_login::run_device_code_login;
use codex_login::run_login_server;
use codex_protocol::auth::AuthMode;
use codex_protocol::config_types::ForcedLoginMethod;
use codex_utils_cli::CliConfigOverrides;
use std::fs::OpenOptions;
+1 -1
View File
@@ -2,7 +2,6 @@ use anyhow::Context;
use anyhow::Result;
use anyhow::bail;
use clap::Parser;
use codex_app_server_protocol::AuthMode;
use codex_core::config::Config;
use codex_core::config::find_codex_home;
use codex_core_plugins::ConfiguredMarketplace;
@@ -22,6 +21,7 @@ use codex_login::CodexAuth;
use codex_login::auth::read_codex_api_key_from_env;
use codex_plugin::PluginId;
use codex_plugin::validate_plugin_segment;
use codex_protocol::auth::AuthMode;
use codex_utils_cli::CliConfigOverrides;
use serde::Serialize;
use std::collections::HashMap;
+1 -1
View File
@@ -1,5 +1,5 @@
use codex_app_server_protocol::AuthMode;
use codex_plugin::AppDeclaration;
use codex_protocol::auth::AuthMode;
use std::collections::HashMap;
use std::collections::HashSet;
@@ -15,9 +15,9 @@ use crate::test_support::write_curated_plugin_sha_with;
use crate::test_support::write_file;
use crate::test_support::write_openai_api_curated_marketplace;
use crate::test_support::write_openai_curated_marketplace;
use codex_app_server_protocol::AuthMode;
use codex_config::CONFIG_TOML_FILE;
use codex_login::CodexAuth;
use codex_protocol::auth::AuthMode;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use serde_json::json;
+1 -1
View File
@@ -15,7 +15,6 @@ use crate::remote::RemoteInstalledPlugin;
use crate::store::PluginStore;
use crate::store::plugin_version_for_source;
use crate::store::plugin_version_for_source_with_fallback_manifest;
use codex_app_server_protocol::AuthMode;
use codex_config::ConfigLayerStack;
use codex_config::HooksFile;
use codex_config::types::McpServerConfig;
@@ -39,6 +38,7 @@ use codex_plugin::PluginHookSource;
use codex_plugin::PluginId;
use codex_plugin::PluginIdError;
use codex_plugin::app_connector_ids_from_declarations;
use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::Product;
use codex_protocol::protocol::SkillScope;
use codex_utils_absolute_path::AbsolutePathBuf;
+1 -1
View File
@@ -54,7 +54,6 @@ use crate::store::PluginStore;
use crate::store::PluginStoreError;
use crate::tool_suggest_metadata::ToolSuggestMetadataCache;
use codex_analytics::AnalyticsEventsClient;
use codex_app_server_protocol::AuthMode;
use codex_config::ConfigLayerStack;
use codex_config::clear_user_plugin;
use codex_config::set_user_plugin_enabled;
@@ -75,6 +74,7 @@ use codex_plugin::PluginIdError;
use codex_plugin::PluginTelemetryMetadata;
use codex_plugin::app_connector_ids_from_declarations;
use codex_plugin::prompt_safe_plugin_description;
use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::HookEventName;
use codex_protocol::protocol::Product;
use codex_tools::DiscoverablePluginInfo;
+1 -1
View File
@@ -25,7 +25,6 @@ use crate::test_support::write_curated_plugin_sha_with as write_curated_plugin_s
use crate::test_support::write_file;
use crate::test_support::write_openai_api_curated_marketplace;
use crate::test_support::write_openai_curated_marketplace;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::ConfigLayerSource;
use codex_config::AppToolApproval;
use codex_config::CONFIG_TOML_FILE;
@@ -44,6 +43,7 @@ use codex_core_skills::config_rules::SkillConfigRules;
use codex_login::CodexAuth;
use codex_plugin::AppDeclaration;
use codex_plugin::PluginId;
use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::HookEventName;
use codex_protocol::protocol::Product;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -2,7 +2,6 @@ use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
use codex_app_server_protocol::AuthMode;
use codex_core_skills::config_rules::SkillConfigRules;
use codex_plugin::AppDeclaration;
use codex_plugin::PluginCapabilitySummary;
@@ -10,6 +9,7 @@ use codex_plugin::PluginId;
use codex_plugin::PluginIdError;
use codex_plugin::app_connector_ids_from_declarations;
use codex_plugin::prompt_safe_plugin_description;
use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::Product;
use tokio::sync::Semaphore;
+1 -1
View File
@@ -61,7 +61,6 @@ use codex_api::auth_header_telemetry;
use codex_api::build_session_headers;
use codex_api::create_text_param_for_request;
use codex_api::response_create_client_metadata;
use codex_app_server_protocol::AuthMode;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_login::RefreshTokenError;
@@ -69,6 +68,7 @@ use codex_login::UnauthorizedRecovery;
use codex_login::default_client::build_reqwest_client;
use codex_otel::SessionTelemetry;
use codex_otel::current_span_w3c_trace_context;
use codex_protocol::auth::AuthMode;
use codex_protocol::ThreadId;
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
+1 -1
View File
@@ -16,7 +16,6 @@ use crate::test_support::responses_metadata as test_responses_metadata;
use codex_api::ApiError;
use codex_api::ResponseEvent;
use codex_api::TransportError;
use codex_app_server_protocol::AuthMode;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_model_provider::BearerAuthProvider;
@@ -28,6 +27,7 @@ use codex_model_provider_info::WireApi;
use codex_model_provider_info::create_oss_provider_with_base_url;
use codex_otel::SessionTelemetry;
use codex_protocol::ThreadId;
use codex_protocol::auth::AuthMode;
use codex_protocol::models::ContentItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ModelInfo;
+1 -1
View File
@@ -25,7 +25,7 @@ use codex_analytics::CompactionImplementation;
use codex_analytics::CompactionPhase;
use codex_analytics::CompactionReason;
use codex_analytics::CompactionTrigger;
use codex_app_server_protocol::AuthMode;
use codex_protocol::auth::AuthMode;
use codex_protocol::error::CodexErr;
use codex_protocol::error::Result as CodexResult;
use codex_protocol::items::ContextCompactionItem;
+1 -1
View File
@@ -22,13 +22,13 @@ use codex_api::RealtimeWebsocketClient;
use codex_api::RealtimeWebsocketEvents;
use codex_api::RealtimeWebsocketWriter;
use codex_api::map_api_error;
use codex_app_server_protocol::AuthMode;
use codex_config::config_toml::RealtimeWsMode;
use codex_config::config_toml::RealtimeWsVersion;
use codex_login::CodexAuth;
use codex_login::default_client::default_headers;
use codex_login::read_openai_api_key_from_env;
use codex_model_provider_info::ModelProviderInfo;
use codex_protocol::auth::AuthMode;
use codex_protocol::error::CodexErr;
use codex_protocol::error::Result as CodexResult;
use codex_protocol::models::MessagePhase;
-1
View File
@@ -11,7 +11,6 @@ workspace = true
base64 = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
codex-agent-identity = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-client = { workspace = true }
codex-config = { workspace = true }
codex-keyring-store = { workspace = true }
+5 -5
View File
@@ -2,8 +2,8 @@ use super::*;
use crate::auth::storage::FileAuthStorage;
use crate::auth::storage::get_auth_file;
use crate::token_data::IdTokenInfo;
use codex_app_server_protocol::AuthMode;
use codex_protocol::account::PlanType as AccountPlanType;
use codex_protocol::auth::AuthMode;
use codex_protocol::auth::KnownPlan as InternalKnownPlan;
use codex_protocol::auth::PlanType as InternalPlanType;
use codex_protocol::protocol::SessionSource;
@@ -162,7 +162,7 @@ async fn stored_agent_identity_jwt_keeps_auth_json_unchanged() -> anyhow::Result
save_auth(
codex_home.path(),
&AuthDotJson {
auth_mode: Some(ApiAuthMode::AgentIdentity),
auth_mode: Some(AuthMode::AgentIdentity),
openai_api_key: None,
tokens: None,
last_refresh: None,
@@ -897,7 +897,7 @@ async fn loads_api_key_from_auth_json() {
fn logout_removes_auth_file() -> Result<(), std::io::Error> {
let dir = tempdir()?;
let auth_dot_json = AuthDotJson {
auth_mode: Some(ApiAuthMode::ApiKey),
auth_mode: Some(AuthMode::ApiKey),
openai_api_key: Some("sk-test-key".to_string()),
tokens: None,
last_refresh: None,
@@ -1045,7 +1045,7 @@ async fn external_bearer_only_auth_manager_uses_cached_provider_token() {
assert_eq!(first.as_deref(), Some("provider-token"));
assert_eq!(second.as_deref(), Some("provider-token"));
assert_eq!(manager.auth_mode(), Some(AuthMode::ApiKey));
assert_eq!(manager.get_api_auth_mode(), Some(ApiAuthMode::ApiKey));
assert_eq!(manager.get_api_auth_mode(), Some(AuthMode::ApiKey));
}
#[tokio::test]
@@ -1822,7 +1822,7 @@ async fn enforce_login_restrictions_logs_out_for_agent_identity_workspace_mismat
save_auth(
codex_home.path(),
&AuthDotJson {
auth_mode: Some(ApiAuthMode::AgentIdentity),
auth_mode: Some(AuthMode::AgentIdentity),
openai_api_key: None,
tokens: None,
last_refresh: None,
+1 -1
View File
@@ -7,7 +7,7 @@ use serde::Serialize;
use super::manager::save_auth;
use super::storage::AuthDotJson;
use super::storage::AuthKeyringBackendKind;
use codex_app_server_protocol::AuthMode;
use codex_protocol::auth::AuthMode;
/// Managed Amazon Bedrock API key persisted in `auth.json`.
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
@@ -1,5 +1,5 @@
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_protocol::auth::AuthMode;
use pretty_assertions::assert_eq;
use serial_test::serial;
use tempfile::tempdir;
+1 -1
View File
@@ -2,7 +2,7 @@ use super::manager::ExternalAuth;
use super::manager::ExternalAuthFuture;
use super::manager::ExternalAuthRefreshContext;
use super::manager::ExternalAuthTokens;
use codex_app_server_protocol::AuthMode;
use codex_protocol::auth::AuthMode;
use codex_protocol::config_types::ModelProviderAuthInfo;
use std::fmt;
use std::io;
+42 -39
View File
@@ -20,8 +20,7 @@ use tokio::sync::watch;
use tracing::instrument;
use codex_agent_identity::ChatGptEnvironment;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::AuthMode as ApiAuthMode;
use codex_protocol::auth::AuthMode;
use codex_protocol::config_types::ForcedLoginMethod;
use codex_protocol::config_types::ModelProviderAuthInfo;
@@ -242,13 +241,13 @@ impl CodexAuth {
auth_route_config: Option<&AuthRouteConfig>,
) -> std::io::Result<Self> {
let auth_mode = auth_dot_json.resolved_mode();
if auth_mode == ApiAuthMode::ApiKey {
if auth_mode == AuthMode::ApiKey {
let Some(api_key) = auth_dot_json.openai_api_key.as_deref() else {
return Err(std::io::Error::other("API key auth is missing a key."));
};
return Ok(Self::from_api_key(api_key));
}
if auth_mode == ApiAuthMode::AgentIdentity {
if auth_mode == AuthMode::AgentIdentity {
let Some(agent_identity) = auth_dot_json.agent_identity.clone() else {
return Err(std::io::Error::other(
"agent identity auth is missing agent identity auth material.",
@@ -282,7 +281,7 @@ impl CodexAuth {
}
}
}
if auth_mode == ApiAuthMode::PersonalAccessToken {
if auth_mode == AuthMode::PersonalAccessToken {
let Some(personal_access_token) = auth_dot_json.personal_access_token.as_deref() else {
return Err(std::io::Error::other(
"personal access token auth is missing a personal access token.",
@@ -291,7 +290,7 @@ impl CodexAuth {
return Self::from_personal_access_token(personal_access_token, auth_route_config)
.await;
}
if auth_mode == ApiAuthMode::BedrockApiKey {
if auth_mode == AuthMode::BedrockApiKey {
let Some(auth) = auth_dot_json.bedrock_api_key else {
return Err(std::io::Error::other(
"Bedrock API key auth is missing a Bedrock API key.",
@@ -308,7 +307,7 @@ impl CodexAuth {
};
match auth_mode {
ApiAuthMode::Chatgpt => {
AuthMode::Chatgpt => {
let storage = create_auth_storage(
codex_home.to_path_buf(),
storage_mode,
@@ -316,15 +315,13 @@ impl CodexAuth {
);
Ok(Self::Chatgpt(ChatgptAuth { state, storage }))
}
ApiAuthMode::ChatgptAuthTokens => {
Ok(Self::ChatgptAuthTokens(ChatgptAuthTokens { state }))
}
ApiAuthMode::ApiKey => unreachable!("api key mode is handled above"),
ApiAuthMode::AgentIdentity => unreachable!("agent identity mode is handled above"),
ApiAuthMode::PersonalAccessToken => {
AuthMode::ChatgptAuthTokens => Ok(Self::ChatgptAuthTokens(ChatgptAuthTokens { state })),
AuthMode::ApiKey => unreachable!("api key mode is handled above"),
AuthMode::AgentIdentity => unreachable!("agent identity mode is handled above"),
AuthMode::PersonalAccessToken => {
unreachable!("personal access token mode is handled above")
}
ApiAuthMode::BedrockApiKey => unreachable!("bedrock api key mode is handled above"),
AuthMode::BedrockApiKey => unreachable!("bedrock api key mode is handled above"),
}
}
@@ -395,6 +392,9 @@ impl CodexAuth {
))
}
/// Returns the effective backend auth mode.
///
/// Externally managed ChatGPT tokens are normalized to [`AuthMode::Chatgpt`].
pub fn auth_mode(&self) -> AuthMode {
match self {
Self::ApiKey(_) => AuthMode::ApiKey,
@@ -405,14 +405,15 @@ impl CodexAuth {
}
}
pub fn api_auth_mode(&self) -> ApiAuthMode {
/// Returns the precise kind of credentials backing this authentication.
pub fn api_auth_mode(&self) -> AuthMode {
match self {
Self::ApiKey(_) => ApiAuthMode::ApiKey,
Self::Chatgpt(_) => ApiAuthMode::Chatgpt,
Self::ChatgptAuthTokens(_) => ApiAuthMode::ChatgptAuthTokens,
Self::AgentIdentity(_) => ApiAuthMode::AgentIdentity,
Self::PersonalAccessToken(_) => ApiAuthMode::PersonalAccessToken,
Self::BedrockApiKey(_) => ApiAuthMode::BedrockApiKey,
Self::ApiKey(_) => AuthMode::ApiKey,
Self::Chatgpt(_) => AuthMode::Chatgpt,
Self::ChatgptAuthTokens(_) => AuthMode::ChatgptAuthTokens,
Self::AgentIdentity(_) => AuthMode::AgentIdentity,
Self::PersonalAccessToken(_) => AuthMode::PersonalAccessToken,
Self::BedrockApiKey(_) => AuthMode::BedrockApiKey,
}
}
@@ -660,7 +661,7 @@ impl CodexAuth {
/// Consider this private to integration tests.
pub fn create_dummy_chatgpt_auth_for_testing() -> Self {
let auth_dot_json = AuthDotJson {
auth_mode: Some(ApiAuthMode::Chatgpt),
auth_mode: Some(AuthMode::Chatgpt),
openai_api_key: None,
tokens: Some(TokenData {
id_token: Default::default(),
@@ -851,7 +852,7 @@ pub fn login_with_api_key(
keyring_backend_kind: AuthKeyringBackendKind,
) -> std::io::Result<()> {
let auth_dot_json = AuthDotJson {
auth_mode: Some(ApiAuthMode::ApiKey),
auth_mode: Some(AuthMode::ApiKey),
openai_api_key: Some(api_key.to_string()),
tokens: None,
last_refresh: None,
@@ -900,7 +901,7 @@ pub async fn login_with_access_token(
.to_string();
verified_record_from_jwt(jwt, &base_url, auth_route_config).await?;
AuthDotJson {
auth_mode: Some(ApiAuthMode::AgentIdentity),
auth_mode: Some(AuthMode::AgentIdentity),
openai_api_key: None,
tokens: None,
last_refresh: None,
@@ -1419,7 +1420,7 @@ impl AuthDotJson {
};
Ok(Self {
auth_mode: Some(ApiAuthMode::ChatgptAuthTokens),
auth_mode: Some(AuthMode::ChatgptAuthTokens),
openai_api_key: None,
tokens: Some(tokens),
last_refresh: Some(Utc::now()),
@@ -1442,27 +1443,27 @@ impl AuthDotJson {
Self::from_external_tokens(&external)
}
pub(super) fn resolved_mode(&self) -> ApiAuthMode {
pub(super) fn resolved_mode(&self) -> AuthMode {
if let Some(mode) = self.auth_mode {
return mode;
}
if self.personal_access_token.is_some() {
return ApiAuthMode::PersonalAccessToken;
return AuthMode::PersonalAccessToken;
}
if self.bedrock_api_key.is_some() {
return ApiAuthMode::BedrockApiKey;
return AuthMode::BedrockApiKey;
}
if self.openai_api_key.is_some() {
return ApiAuthMode::ApiKey;
return AuthMode::ApiKey;
}
ApiAuthMode::Chatgpt
AuthMode::Chatgpt
}
fn storage_mode(
&self,
auth_credentials_store_mode: AuthCredentialsStoreMode,
) -> AuthCredentialsStoreMode {
if self.resolved_mode() == ApiAuthMode::ChatgptAuthTokens {
if self.resolved_mode() == AuthMode::ChatgptAuthTokens {
AuthCredentialsStoreMode::Ephemeral
} else {
auth_credentials_store_mode
@@ -2072,19 +2073,19 @@ impl AuthManager {
match (a, b) {
(None, None) => true,
(Some(a), Some(b)) => match (a.api_auth_mode(), b.api_auth_mode()) {
(ApiAuthMode::ApiKey, ApiAuthMode::ApiKey) => a.api_key() == b.api_key(),
(ApiAuthMode::Chatgpt, ApiAuthMode::Chatgpt)
| (ApiAuthMode::ChatgptAuthTokens, ApiAuthMode::ChatgptAuthTokens) => {
(AuthMode::ApiKey, AuthMode::ApiKey) => a.api_key() == b.api_key(),
(AuthMode::Chatgpt, AuthMode::Chatgpt)
| (AuthMode::ChatgptAuthTokens, AuthMode::ChatgptAuthTokens) => {
a.get_current_auth_json() == b.get_current_auth_json()
}
(ApiAuthMode::AgentIdentity, ApiAuthMode::AgentIdentity) => match (a, b) {
(AuthMode::AgentIdentity, AuthMode::AgentIdentity) => match (a, b) {
(CodexAuth::AgentIdentity(a), CodexAuth::AgentIdentity(b)) => {
a.record() == b.record()
}
_ => false,
},
(ApiAuthMode::PersonalAccessToken, ApiAuthMode::PersonalAccessToken) => a == b,
(ApiAuthMode::BedrockApiKey, ApiAuthMode::BedrockApiKey) => a == b,
(AuthMode::PersonalAccessToken, AuthMode::PersonalAccessToken) => a == b,
(AuthMode::BedrockApiKey, AuthMode::BedrockApiKey) => a == b,
_ => false,
},
_ => false,
@@ -2401,13 +2402,15 @@ impl AuthManager {
Ok(result)
}
pub fn get_api_auth_mode(&self) -> Option<ApiAuthMode> {
/// Returns the precise kind of credentials backing the current authentication.
pub fn get_api_auth_mode(&self) -> Option<AuthMode> {
if self.has_external_api_key_auth() {
return Some(ApiAuthMode::ApiKey);
return Some(AuthMode::ApiKey);
}
self.auth_cached().as_ref().map(CodexAuth::api_auth_mode)
}
/// Returns the effective backend auth mode for the current authentication.
pub fn auth_mode(&self) -> Option<AuthMode> {
if self.has_external_api_key_auth() {
return Some(AuthMode::ApiKey);
+5 -5
View File
@@ -7,8 +7,8 @@
use serde::Serialize;
use std::time::Duration;
use codex_app_server_protocol::AuthMode as ApiAuthMode;
use codex_client::CodexHttpClient;
use codex_protocol::auth::AuthMode;
use super::manager::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
use super::manager::REVOKE_TOKEN_URL;
@@ -77,21 +77,21 @@ fn revocable_token(auth_dot_json: &AuthDotJson) -> Option<(&str, RevokeTokenKind
}
fn managed_chatgpt_tokens(auth_dot_json: &AuthDotJson) -> Option<&TokenData> {
if resolved_auth_mode(auth_dot_json) == ApiAuthMode::Chatgpt {
if resolved_auth_mode(auth_dot_json) == AuthMode::Chatgpt {
auth_dot_json.tokens.as_ref()
} else {
None
}
}
fn resolved_auth_mode(auth_dot_json: &AuthDotJson) -> ApiAuthMode {
fn resolved_auth_mode(auth_dot_json: &AuthDotJson) -> AuthMode {
if let Some(mode) = auth_dot_json.auth_mode {
return mode;
}
if auth_dot_json.openai_api_key.is_some() {
return ApiAuthMode::ApiKey;
return AuthMode::ApiKey;
}
ApiAuthMode::Chatgpt
AuthMode::Chatgpt
}
async fn revoke_oauth_token(
+1 -1
View File
@@ -22,12 +22,12 @@ use super::BedrockApiKeyAuth;
use crate::token_data::TokenData;
use codex_agent_identity::AgentIdentityJwtClaims;
use codex_agent_identity::decode_agent_identity_jwt;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
pub use codex_config::types::AuthKeyringBackendKind;
use codex_keyring_store::DefaultKeyringStore;
use codex_keyring_store::KeyringStore;
use codex_protocol::account::PlanType as AccountPlanType;
use codex_protocol::auth::AuthMode;
use codex_secrets::LocalSecretsNamespace;
use codex_secrets::SecretName;
use codex_secrets::SecretScope;
+1 -1
View File
@@ -36,8 +36,8 @@ use crate::token_data::TokenData;
use crate::token_data::parse_chatgpt_jwt_claims;
use base64::Engine;
use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_protocol::auth::AuthMode;
use codex_utils_template::Template;
use rand::RngCore;
use serde_json::Value as JsonValue;
+1 -1
View File
@@ -3,7 +3,6 @@ use anyhow::Result;
use base64::Engine;
use chrono::Duration;
use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
@@ -15,6 +14,7 @@ use codex_login::load_auth_dot_json;
use codex_login::save_auth;
use codex_login::token_data::IdTokenInfo;
use codex_login::token_data::TokenData;
use codex_protocol::auth::AuthMode;
use codex_protocol::auth::RefreshTokenFailedReason;
use core_test_support::skip_if_no_network;
use pretty_assertions::assert_eq;
+1 -1
View File
@@ -1,7 +1,6 @@
use anyhow::Context;
use anyhow::Result;
use base64::Engine;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthDotJson;
use codex_login::AuthKeyringBackendKind;
@@ -14,6 +13,7 @@ use codex_login::logout_with_revoke;
use codex_login::save_auth;
use codex_login::token_data::IdTokenInfo;
use codex_login::token_data::TokenData;
use codex_protocol::auth::AuthMode;
use core_test_support::skip_if_no_network;
use pretty_assertions::assert_eq;
use serde_json::Value;
-1
View File
@@ -14,7 +14,6 @@ workspace = true
[dependencies]
codex-api = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-protocol = { workspace = true }
http = { workspace = true }
schemars = { workspace = true }
+1 -1
View File
@@ -8,7 +8,7 @@
use codex_api::Provider as ApiProvider;
use codex_api::RetryConfig as ApiRetryConfig;
use codex_api::is_azure_responses_provider;
use codex_app_server_protocol::AuthMode;
use codex_protocol::auth::AuthMode;
use codex_protocol::config_types::ModelProviderAuthInfo;
use codex_protocol::error::CodexErr;
use codex_protocol::error::EnvVarError;
-1
View File
@@ -14,7 +14,6 @@ workspace = true
[dependencies]
chrono = { workspace = true, features = ["serde"] }
codex-app-server-protocol = { workspace = true }
codex-collaboration-mode-templates = { workspace = true }
codex-login = { workspace = true }
codex-otel = { workspace = true }
+1 -1
View File
@@ -6,7 +6,7 @@ pub mod model_info;
pub mod model_presets;
pub mod test_support;
pub use codex_app_server_protocol::AuthMode;
pub use codex_protocol::auth::AuthMode;
pub use config::ModelsManagerConfig;
/// Load the bundled model catalog shipped with `codex-models-manager`.
+1 -1
View File
@@ -2,8 +2,8 @@ use super::cache::ModelsCacheManager;
use crate::collaboration_mode_presets::builtin_collaboration_mode_presets;
use crate::config::ModelsManagerConfig;
use crate::model_info;
use codex_app_server_protocol::AuthMode;
use codex_login::AuthManager;
use codex_protocol::auth::AuthMode;
use codex_protocol::config_types::CollaborationModeMask;
use codex_protocol::error::Result as CoreResult;
use codex_protocol::openai_models::ModelInfo;
+1 -1
View File
@@ -1,7 +1,6 @@
use super::*;
use crate::ModelsManagerConfig;
use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_login::AuthCredentialsStoreMode;
use codex_login::AuthKeyringBackendKind;
use codex_login::AuthManager;
@@ -10,6 +9,7 @@ use codex_login::ExternalAuth;
use codex_login::ExternalAuthRefreshContext;
use codex_login::ExternalAuthTokens;
use codex_login::TokenData;
use codex_protocol::auth::AuthMode;
use codex_protocol::openai_models::ModelsResponse;
use pretty_assertions::assert_eq;
use serde_json::json;
-1
View File
@@ -17,7 +17,6 @@ chrono = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-string = { workspace = true }
codex-api = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-protocol = { workspace = true }
eventsource-stream = { workspace = true }
gethostname = { workspace = true }
+9 -9
View File
@@ -8,6 +8,7 @@ mod otlp;
mod targets;
use crate::metrics::Result as MetricsResult;
use codex_protocol::auth::AuthMode;
use serde::Serialize;
use strum_macros::Display;
@@ -44,22 +45,21 @@ pub enum ToolDecisionSource {
User,
}
/// Maps to API/auth `AuthMode` to avoid a circular dependency on codex-core.
/// Coarsens the authentication domain into the dimensions used by telemetry.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
pub enum TelemetryAuthMode {
ApiKey,
Chatgpt,
}
impl From<codex_app_server_protocol::AuthMode> for TelemetryAuthMode {
fn from(mode: codex_app_server_protocol::AuthMode) -> Self {
impl From<AuthMode> for TelemetryAuthMode {
fn from(mode: AuthMode) -> Self {
match mode {
codex_app_server_protocol::AuthMode::ApiKey
| codex_app_server_protocol::AuthMode::BedrockApiKey => Self::ApiKey,
codex_app_server_protocol::AuthMode::Chatgpt
| codex_app_server_protocol::AuthMode::ChatgptAuthTokens
| codex_app_server_protocol::AuthMode::AgentIdentity
| codex_app_server_protocol::AuthMode::PersonalAccessToken => Self::Chatgpt,
AuthMode::ApiKey | AuthMode::BedrockApiKey => Self::ApiKey,
AuthMode::Chatgpt
| AuthMode::ChatgptAuthTokens
| AuthMode::AgentIdentity
| AuthMode::PersonalAccessToken => Self::Chatgpt,
}
}
}
+48
View File
@@ -1,7 +1,55 @@
use serde::Deserialize;
use serde::Serialize;
use strum_macros::Display;
use thiserror::Error;
/// Authentication mode for OpenAI-backed providers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuthMode {
/// OpenAI API key provided by the caller and stored by Codex.
ApiKey,
/// ChatGPT OAuth managed by Codex (tokens persisted and refreshed by Codex).
Chatgpt,
/// ChatGPT auth tokens supplied by an external host application.
#[serde(rename = "chatgptAuthTokens")]
#[strum(serialize = "chatgptAuthTokens")]
ChatgptAuthTokens,
/// Programmatic Codex auth backed by a registered Agent Identity.
#[serde(rename = "agentIdentity")]
#[strum(serialize = "agentIdentity")]
AgentIdentity,
/// Programmatic Codex auth backed by a personal access token.
#[serde(rename = "personalAccessToken")]
#[strum(serialize = "personalAccessToken")]
PersonalAccessToken,
/// Amazon Bedrock bearer token managed by Codex.
#[serde(rename = "bedrockApiKey")]
#[strum(serialize = "bedrockApiKey")]
BedrockApiKey,
}
impl AuthMode {
/// Returns whether this mode represents an authenticated human ChatGPT account.
pub fn has_chatgpt_account(self) -> bool {
match self {
Self::Chatgpt | Self::ChatgptAuthTokens | Self::PersonalAccessToken => true,
Self::ApiKey | Self::AgentIdentity | Self::BedrockApiKey => false,
}
}
/// Returns whether this mode is backed by Codex services rather than a direct model API.
pub fn uses_codex_backend(self) -> bool {
match self {
Self::Chatgpt
| Self::ChatgptAuthTokens
| Self::AgentIdentity
| Self::PersonalAccessToken => true,
Self::ApiKey | Self::BedrockApiKey => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PlanType {
+4 -4
View File
@@ -32,7 +32,6 @@ use codex_app_server_client::RemoteAppServerConnectArgs;
pub use codex_app_server_client::RemoteAppServerEndpoint;
use codex_app_server_protocol::Account as AppServerAccount;
use codex_app_server_protocol::AskForApproval;
use codex_app_server_protocol::AuthMode as AppServerAuthMode;
use codex_app_server_protocol::ConfigWarningNotification;
use codex_app_server_protocol::Thread as AppServerThread;
use codex_app_server_protocol::ThreadListCwdFilter;
@@ -51,6 +50,7 @@ use codex_login::default_client::originator;
use codex_login::default_client::set_default_client_residency_requirement;
use codex_login::enforce_login_restrictions;
use codex_protocol::ThreadId;
use codex_protocol::auth::AuthMode;
use codex_protocol::config_types::AltScreenMode;
use codex_protocol::config_types::SandboxMode;
#[cfg(target_os = "windows")]
@@ -1869,7 +1869,7 @@ fn determine_alt_screen_mode(no_alt_screen: bool, tui_alternate_screen: AltScree
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoginStatus {
AuthMode(AppServerAuthMode),
AuthMode(AuthMode),
NotAuthenticated,
}
@@ -1886,8 +1886,8 @@ async fn get_login_status(
let account = app_server.read_account().await?;
Ok(match account.account {
Some(AppServerAccount::ApiKey {}) => LoginStatus::AuthMode(AppServerAuthMode::ApiKey),
Some(AppServerAccount::Chatgpt { .. }) => LoginStatus::AuthMode(AppServerAuthMode::Chatgpt),
Some(AppServerAccount::ApiKey {}) => LoginStatus::AuthMode(AuthMode::ApiKey),
Some(AppServerAccount::Chatgpt { .. }) => LoginStatus::AuthMode(AuthMode::Chatgpt),
Some(AppServerAccount::AmazonBedrock { .. }) => LoginStatus::NotAuthenticated,
None => LoginStatus::NotAuthenticated,
})
+2 -2
View File
@@ -2,10 +2,10 @@
use std::path::Path;
use codex_app_server_protocol::AuthMode;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::AuthKeyringBackendKind;
use codex_login::load_auth_dot_json;
use codex_protocol::auth::AuthMode;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct LocalChatgptAuth {
@@ -64,11 +64,11 @@ mod tests {
use base64::Engine;
use chrono::Utc;
use codex_app_server_protocol::AuthMode;
use codex_login::AuthDotJson;
use codex_login::auth::login_with_chatgpt_auth_tokens;
use codex_login::save_auth;
use codex_login::token_data::TokenData;
use codex_protocol::auth::AuthMode;
use pretty_assertions::assert_eq;
use serde::Serialize;
use serde_json::json;
+13 -7
View File
@@ -10,13 +10,13 @@
use codex_app_server_client::AppServerRequestHandle;
use codex_app_server_protocol::AccountLoginCompletedNotification;
use codex_app_server_protocol::AccountUpdatedNotification;
#[cfg(test)]
use codex_app_server_protocol::AuthMode as AppServerAuthMode;
use codex_app_server_protocol::AuthMode as ApiAuthMode;
use codex_app_server_protocol::CancelLoginAccountParams;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::LoginAccountParams;
use codex_app_server_protocol::LoginAccountResponse;
use codex_login::read_openai_api_key_from_env;
use codex_protocol::auth::AuthMode;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyEventKind;
@@ -945,7 +945,16 @@ impl AuthModeWidget {
pub(crate) fn on_account_updated(&mut self, notification: AccountUpdatedNotification) {
self.login_status = notification
.auth_mode
.map(LoginStatus::AuthMode)
.map(|auth_mode| {
LoginStatus::AuthMode(match auth_mode {
ApiAuthMode::ApiKey => AuthMode::ApiKey,
ApiAuthMode::Chatgpt => AuthMode::Chatgpt,
ApiAuthMode::ChatgptAuthTokens => AuthMode::ChatgptAuthTokens,
ApiAuthMode::AgentIdentity => AuthMode::AgentIdentity,
ApiAuthMode::PersonalAccessToken => AuthMode::PersonalAccessToken,
ApiAuthMode::BedrockApiKey => AuthMode::BedrockApiKey,
})
})
.unwrap_or(LoginStatus::NotAuthenticated);
}
}
@@ -1111,10 +1120,7 @@ mod tests {
#[tokio::test]
async fn existing_non_oauth_chatgpt_login_counts_as_signed_in() {
for auth_mode in [
AppServerAuthMode::ChatgptAuthTokens,
AppServerAuthMode::PersonalAccessToken,
] {
for auth_mode in [AuthMode::ChatgptAuthTokens, AuthMode::PersonalAccessToken] {
let (mut widget, _tmp) = widget_forced_chatgpt().await;
widget.login_status = LoginStatus::AuthMode(auth_mode);