Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions codex-rs/app-server-transport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
19 changes: 19 additions & 0 deletions codex-rs/app-server/src/auth_mode.rs
Original file line number Diff line number Diff line change
@@ -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 {
Comment thread
anp-oai marked this conversation as resolved.
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 change: 1 addition & 0 deletions codex-rs/app-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/app-server/src/message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 12 additions & 4 deletions codex-rs/app-server/src/request_processors/account_processor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::*;
use crate::auth_mode::auth_mode_to_api;
use chrono::DateTime;

mod rate_limit_resets;
Expand Down Expand Up @@ -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),
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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(_)
Expand Down
3 changes: 2 additions & 1 deletion codex-rs/app-server/src/request_processors/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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(),
};
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/app-server/tests/common/auth_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/app-server/tests/suite/v2/app_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
66 changes: 32 additions & 34 deletions codex-rs/cli/src/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
}

Expand All @@ -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()
Expand All @@ -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() {
Expand All @@ -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() {
Expand All @@ -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()
Expand All @@ -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()
Expand All @@ -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");
}
Expand Down Expand Up @@ -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",
}
}

Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand All @@ -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,
Expand Down
Loading
Loading