mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Migrate agent identity to the shared HTTP client (#34631)
## Why `codex-http-client` is the intended owner of direct `reqwest` usage. ## What changed - Route agent identity registration, task registration, and JWKS requests through `HttpClient`. - Preserve retry classification using `HttpError` and `http::StatusCode`. - Remove `codex-agent-identity` from the temporary direct-`reqwest` dependency exceptions. GitOrigin-RevId: 7445c9f2521e6d8ef0969a1483c81f797bf91316
This commit is contained in:
Generated
+2
-1
@@ -1868,13 +1868,14 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"codex-http-client",
|
||||
"codex-protocol",
|
||||
"crypto_box",
|
||||
"ed25519-dalek",
|
||||
"http 1.4.0",
|
||||
"jsonwebtoken",
|
||||
"pretty_assertions",
|
||||
"rand 0.9.3",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.9",
|
||||
|
||||
@@ -16,12 +16,13 @@ workspace = true
|
||||
anyhow = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
codex-http-client = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
crypto_box = { workspace = true }
|
||||
ed25519-dalek = { workspace = true }
|
||||
http = { workspace = true }
|
||||
jsonwebtoken = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
|
||||
@@ -10,6 +10,8 @@ use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
||||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||
use chrono::SecondsFormat;
|
||||
use chrono::Utc;
|
||||
use codex_http_client::HttpClient;
|
||||
use codex_http_client::HttpError;
|
||||
use codex_protocol::auth::PlanType as AuthPlanType;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use crypto_box::SecretKey as Curve25519SecretKey;
|
||||
@@ -18,6 +20,7 @@ use ed25519_dalek::SigningKey;
|
||||
use ed25519_dalek::VerifyingKey;
|
||||
use ed25519_dalek::pkcs8::DecodePrivateKey;
|
||||
use ed25519_dalek::pkcs8::EncodePrivateKey;
|
||||
use http::StatusCode;
|
||||
use jsonwebtoken::Algorithm;
|
||||
use jsonwebtoken::DecodingKey;
|
||||
use jsonwebtoken::Validation;
|
||||
@@ -167,12 +170,12 @@ struct RegisterAgentResponse {
|
||||
#[derive(Debug)]
|
||||
pub struct AgentIdentityRegistrationHttpError {
|
||||
operation: &'static str,
|
||||
status: reqwest::StatusCode,
|
||||
status: StatusCode,
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl AgentIdentityRegistrationHttpError {
|
||||
fn new(operation: &'static str, status: reqwest::StatusCode, body: String) -> Self {
|
||||
fn new(operation: &'static str, status: StatusCode, body: String) -> Self {
|
||||
Self {
|
||||
operation,
|
||||
status,
|
||||
@@ -181,7 +184,7 @@ impl AgentIdentityRegistrationHttpError {
|
||||
}
|
||||
|
||||
/// HTTP status returned by the registration endpoint.
|
||||
pub fn status(&self) -> reqwest::StatusCode {
|
||||
pub fn status(&self) -> StatusCode {
|
||||
self.status
|
||||
}
|
||||
}
|
||||
@@ -212,7 +215,7 @@ fn is_retryable_registration_cause(cause: &(dyn StdError + 'static)) -> bool {
|
||||
return is_retryable_registration_status(error.status());
|
||||
}
|
||||
|
||||
if let Some(error) = cause.downcast_ref::<reqwest::Error>() {
|
||||
if let Some(error) = cause.downcast_ref::<HttpError>() {
|
||||
if let Some(status) = error.status() {
|
||||
return is_retryable_registration_status(status);
|
||||
}
|
||||
@@ -222,8 +225,8 @@ fn is_retryable_registration_cause(cause: &(dyn StdError + 'static)) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_retryable_registration_status(status: reqwest::StatusCode) -> bool {
|
||||
status == reqwest::StatusCode::TOO_MANY_REQUESTS || status.is_server_error()
|
||||
fn is_retryable_registration_status(status: StatusCode) -> bool {
|
||||
status == StatusCode::TOO_MANY_REQUESTS || status.is_server_error()
|
||||
}
|
||||
|
||||
pub fn authorization_header_for_agent_task(
|
||||
@@ -242,7 +245,7 @@ pub fn authorization_header_for_agent_task(
|
||||
}
|
||||
|
||||
pub async fn fetch_agent_identity_jwks(
|
||||
client: &reqwest::Client,
|
||||
client: &HttpClient,
|
||||
agent_identity_jwt_base_url: &str,
|
||||
) -> Result<JwkSet> {
|
||||
let response = client
|
||||
@@ -310,7 +313,7 @@ pub fn sign_task_registration_payload(
|
||||
}
|
||||
|
||||
pub async fn register_agent_task(
|
||||
client: &reqwest::Client,
|
||||
client: &HttpClient,
|
||||
agent_identity_authapi_base_url: &str,
|
||||
key: AgentIdentityKey<'_>,
|
||||
) -> Result<String> {
|
||||
@@ -353,7 +356,7 @@ pub async fn register_agent_task(
|
||||
}
|
||||
|
||||
pub async fn register_agent_identity(
|
||||
client: &reqwest::Client,
|
||||
client: &HttpClient,
|
||||
agent_identity_authapi_base_url: &str,
|
||||
access_token: &str,
|
||||
is_fedramp_account: bool,
|
||||
@@ -937,12 +940,12 @@ J1bwkqKZTB5dHolX9A58e/xXnfZ5P8f3Z83+Izap3FwqQulk7b1WO1MQcHuVg2NN
|
||||
fn retryable_registration_error_accepts_429_and_5xx() {
|
||||
let too_many_requests = anyhow::Error::new(AgentIdentityRegistrationHttpError::new(
|
||||
"agent registration",
|
||||
reqwest::StatusCode::TOO_MANY_REQUESTS,
|
||||
StatusCode::TOO_MANY_REQUESTS,
|
||||
"rate limited".to_string(),
|
||||
));
|
||||
let unavailable = anyhow::Error::new(AgentIdentityRegistrationHttpError::new(
|
||||
"agent registration",
|
||||
reqwest::StatusCode::SERVICE_UNAVAILABLE,
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
"try later".to_string(),
|
||||
));
|
||||
|
||||
@@ -954,7 +957,7 @@ J1bwkqKZTB5dHolX9A58e/xXnfZ5P8f3Z83+Izap3FwqQulk7b1WO1MQcHuVg2NN
|
||||
fn retryable_registration_error_rejects_hard_failures() {
|
||||
let forbidden = anyhow::Error::new(AgentIdentityRegistrationHttpError::new(
|
||||
"agent registration",
|
||||
reqwest::StatusCode::FORBIDDEN,
|
||||
StatusCode::FORBIDDEN,
|
||||
"not allowed".to_string(),
|
||||
));
|
||||
let malformed = anyhow::anyhow!("failed to sign registration request");
|
||||
|
||||
@@ -239,7 +239,6 @@ deny = [
|
||||
# Intended owner.
|
||||
"codex-http-client",
|
||||
# Temporary migration exceptions.
|
||||
"codex-agent-identity",
|
||||
"codex-api",
|
||||
"codex-app-server",
|
||||
"codex-core",
|
||||
|
||||
@@ -14,11 +14,12 @@ use codex_agent_identity::is_retryable_registration_error;
|
||||
use codex_agent_identity::public_key_ssh_from_private_key_pkcs8_base64;
|
||||
use codex_agent_identity::register_agent_identity;
|
||||
use codex_agent_identity::register_agent_task;
|
||||
use codex_http_client::HttpClient;
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::default_client::build_default_auth_reqwest_client;
|
||||
use crate::default_client::create_default_auth_client;
|
||||
use crate::outbound_proxy::AuthRouteConfig;
|
||||
|
||||
use super::storage::AgentIdentityAuthRecord;
|
||||
@@ -178,7 +179,7 @@ pub(super) async fn register_managed_chatgpt_agent_identity(
|
||||
) -> std::io::Result<AgentIdentityAuth> {
|
||||
let key_material = generate_agent_key_material().map_err(std::io::Error::other)?;
|
||||
let registration_url = agent_registration_url(agent_identity_authapi_base_url);
|
||||
let client = build_default_auth_reqwest_client(®istration_url, auth_route_config)?;
|
||||
let client = create_default_auth_client(®istration_url, auth_route_config)?;
|
||||
let runtime_id = retry_registration(|| async {
|
||||
register_agent_identity(
|
||||
&client,
|
||||
@@ -225,7 +226,7 @@ pub(super) async fn verified_record_from_jwt(
|
||||
) -> std::io::Result<AgentIdentityAuthRecord> {
|
||||
AgentIdentityAuthRecord::from_agent_identity_jwt(jwt)?;
|
||||
let jwks_url = agent_identity_jwks_url(chatgpt_base_url);
|
||||
let client = build_default_auth_reqwest_client(&jwks_url, auth_route_config)?;
|
||||
let client = create_default_auth_client(&jwks_url, auth_route_config)?;
|
||||
let jwks = fetch_agent_identity_jwks(&client, chatgpt_base_url)
|
||||
.await
|
||||
.map_err(std::io::Error::other)?;
|
||||
@@ -305,7 +306,7 @@ async fn register_task_for_record_with_retries(
|
||||
) -> std::io::Result<String> {
|
||||
let task_registration_url =
|
||||
agent_task_registration_url(agent_identity_authapi_base_url, &record.agent_runtime_id);
|
||||
let client = build_default_auth_reqwest_client(&task_registration_url, auth_route_config)?;
|
||||
let client = create_default_auth_client(&task_registration_url, auth_route_config)?;
|
||||
retry_registration(|| async {
|
||||
register_task_for_record(&client, record, agent_identity_authapi_base_url).await
|
||||
})
|
||||
@@ -313,7 +314,7 @@ async fn register_task_for_record_with_retries(
|
||||
}
|
||||
|
||||
async fn register_task_for_record(
|
||||
client: &reqwest::Client,
|
||||
client: &HttpClient,
|
||||
record: &AgentIdentityAuthRecord,
|
||||
agent_identity_authapi_base_url: &str,
|
||||
) -> std::io::Result<String> {
|
||||
|
||||
Reference in New Issue
Block a user