mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
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:
1 parent
806a4b66c9
commit
31372078d1
47 files changed
+224
-146
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user