Skip to content

Commit 5f089f4

Browse files
committed
Align agent task registration auth
1 parent 7e264d6 commit 5f089f4

3 files changed

Lines changed: 34 additions & 16 deletions

File tree

codex-rs/core/src/agent_identity.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ mod tests {
737737
chatgpt_plan_type: None,
738738
chatgpt_user_id: user_id.map(ToOwned::to_owned),
739739
chatgpt_account_id: Some(account_id.to_string()),
740+
is_org_owner: None,
740741
raw_jwt: fake_id_token(account_id, user_id),
741742
},
742743
access_token: format!("access-token-{account_id}"),

codex-rs/core/src/agent_identity/task_registration.rs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ impl AgentIdentityManager {
5252
};
5353

5454
let client = create_client();
55-
let url =
56-
agent_task_registration_url(&self.chatgpt_base_url, &stored_identity.agent_runtime_id);
55+
let url = agent_task_registration_url(
56+
&self.agent_identity_base_url,
57+
&stored_identity.agent_runtime_id,
58+
);
59+
let human_biscuit = self.mint_human_biscuit(&binding).await?;
5760
let response = client
5861
.post(&url)
59-
.bearer_auth(&binding.access_token)
60-
.header("chatgpt-account-id", &binding.chatgpt_account_id)
62+
.header("X-OpenAI-Authorization", human_biscuit)
6163
.json(&request_body)
6264
.timeout(AGENT_TASK_REGISTRATION_TIMEOUT)
6365
.send()
@@ -124,13 +126,9 @@ fn curve25519_secret_key_from_signing_key(signing_key: &SigningKey) -> Curve2551
124126
Curve25519SecretKey::from(secret_key)
125127
}
126128

127-
fn agent_task_registration_url(chatgpt_base_url: &str, agent_runtime_id: &str) -> String {
128-
let trimmed = chatgpt_base_url.trim_end_matches('/');
129-
let path = format!("/v1/agent/{agent_runtime_id}/task/register");
130-
if let Some(root) = trimmed.strip_suffix("/backend-api") {
131-
return format!("{root}{path}");
132-
}
133-
format!("{trimmed}{path}")
129+
fn agent_task_registration_url(agent_identity_base_url: &str, agent_runtime_id: &str) -> String {
130+
let trimmed = agent_identity_base_url.trim_end_matches('/');
131+
format!("{trimmed}/v1/agent/{agent_runtime_id}/task/register")
134132
}
135133

136134
#[cfg(test)]
@@ -201,6 +199,7 @@ mod tests {
201199
#[tokio::test]
202200
async fn register_task_registers_and_decrypts_plaintext_task_id() {
203201
let server = MockServer::start().await;
202+
mount_human_biscuit(&server).await;
204203
let tempdir = tempfile::tempdir().expect("tempdir");
205204
let keyring_store = Arc::new(MockKeyringStore::default());
206205
let secrets_manager = SecretsManager::new_with_keyring_store(
@@ -213,7 +212,7 @@ mod tests {
213212
let manager = AgentIdentityManager::new_for_tests(
214213
auth_manager,
215214
/*feature_enabled*/ true,
216-
format!("{}/backend-api/", server.uri()),
215+
server.uri(),
217216
SessionSource::Cli,
218217
secrets_manager.clone(),
219218
);
@@ -224,8 +223,7 @@ mod tests {
224223

225224
Mock::given(method("POST"))
226225
.and(path("/v1/agent/agent-123/task/register"))
227-
.and(header("authorization", "Bearer access-token-account-123"))
228-
.and(header("chatgpt-account-id", "account-123"))
226+
.and(header("x-openai-authorization", "human-biscuit"))
229227
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
230228
"encrypted_task_id": encrypted_task_id,
231229
})))
@@ -250,8 +248,9 @@ mod tests {
250248
}
251249

252250
#[tokio::test]
253-
async fn register_task_uses_canonical_registration_url() {
251+
async fn register_task_uses_agent_identity_base_url() {
254252
let server = MockServer::start().await;
253+
mount_human_biscuit(&server).await;
255254
let tempdir = tempfile::tempdir().expect("tempdir");
256255
let keyring_store = Arc::new(MockKeyringStore::default());
257256
let secrets_manager = SecretsManager::new_with_keyring_store(
@@ -264,7 +263,7 @@ mod tests {
264263
let manager = AgentIdentityManager::new_for_tests(
265264
auth_manager,
266265
/*feature_enabled*/ true,
267-
format!("{}/backend-api/", server.uri()),
266+
server.uri(),
268267
SessionSource::Cli,
269268
secrets_manager.clone(),
270269
);
@@ -275,6 +274,7 @@ mod tests {
275274

276275
Mock::given(method("POST"))
277276
.and(path("/v1/agent/agent-fallback/task/register"))
277+
.and(header("x-openai-authorization", "human-biscuit"))
278278
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
279279
"encrypted_task_id": encrypted_task_id,
280280
})))
@@ -292,6 +292,20 @@ mod tests {
292292
assert_eq!(task.task_id, "task_fallback");
293293
}
294294

295+
async fn mount_human_biscuit(server: &MockServer) {
296+
Mock::given(method("GET"))
297+
.and(path("/authenticate_app_v2"))
298+
.and(header("authorization", "Bearer access-token-account-123"))
299+
.and(header("x-original-method", "GET"))
300+
.and(header("x-original-url", AGENT_IDENTITY_BISCUIT_TARGET_URL))
301+
.respond_with(
302+
ResponseTemplate::new(200).insert_header("x-openai-authorization", "human-biscuit"),
303+
)
304+
.expect(1)
305+
.mount(server)
306+
.await;
307+
}
308+
295309
fn seed_stored_identity(
296310
manager: &AgentIdentityManager,
297311
secrets_manager: &SecretsManager,
@@ -350,6 +364,7 @@ mod tests {
350364
chatgpt_plan_type: None,
351365
chatgpt_user_id: user_id.map(ToOwned::to_owned),
352366
chatgpt_account_id: Some(account_id.to_string()),
367+
is_org_owner: None,
353368
raw_jwt: fake_id_token(account_id, user_id),
354369
},
355370
access_token: format!("access-token-{account_id}"),

codex-rs/core/src/codex.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6316,6 +6316,8 @@ pub(crate) async fn run_turn(
63166316
}
63176317
if let Err(error) = sess.ensure_agent_task_registered().await {
63186318
warn!(error = %error, "agent task registration failed");
6319+
sess.fail_agent_identity_registration(error).await;
6320+
return None;
63196321
}
63206322

63216323
if !skill_items.is_empty() {

0 commit comments

Comments
 (0)