[codex] Use AgentAssertion downstream behind use_agent_identity (#17980)

## Summary

This is the AgentAssertion downstream slice for feature-gated agent
identity support, replacing the oversized AgentAssertion slice from PR
#17807.

It isolates task-scoped downstream AgentAssertion wiring on top of the
merged PR3.1 work without re-carrying the earlier agent registration,
task registration, or task-state history.

This PR includes the task-scoped bug-fix call sites from the review:
generic file upload auth, MCP OpenAI file upload auth, and ARC monitor
auth. Broader user/control-plane calls move to PR4.1 and PR4.2.

## Stack

- PR1: https://github.com/openai/codex/pull/17385 - add
`features.use_agent_identity`
- PR2: https://github.com/openai/codex/pull/17386 - register agent
identities when enabled
- PR3: https://github.com/openai/codex/pull/17387 - register agent tasks
when enabled
- PR3.1: https://github.com/openai/codex/pull/17978 - persist and
prewarm registered tasks per thread
- PR4: this PR - use task-scoped `AgentAssertion` downstream when
enabled
- PR4.1: https://github.com/openai/codex/pull/18094 - introduce
AuthManager-owned background/control-plane `AgentAssertion` auth
- PR4.2: https://github.com/openai/codex/pull/18260 - use background
task auth for additional backend/control-plane calls

## What Changed

- add AgentAssertion envelope generation in `codex-core`
- route downstream HTTP and websocket auth through AgentAssertion when
an agent task is present
- extend the model-provider auth provider so non-bearer authorization
schemes can be passed through cleanly
- make generic file uploads attach the full authorization header value
- make MCP OpenAI file uploads use the cached thread agent task
assertion when present
- make ARC monitor calls use the cached thread agent task assertion when
present

## Why

The original PR had drifted ancestry and showed a much larger diff than
the semantic change actually required. Restacking it onto PR3.1 keeps
the reviewable surface down to the downstream assertion slice.

## Validation

- `just fmt`
- `cargo check -p codex-core -p codex-login -p codex-analytics -p
codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
codex-models-manager -p codex-chatgpt -p codex-model-provider -p
codex-mcp -p codex-core-skills`
- `cargo test -p codex-model-provider bearer_auth_provider`
- `cargo test -p codex-core agent_assertion`
- `cargo test -p codex-app-server remote_control`
- `cargo test -p codex-cloud-requirements fetch_cloud_requirements`
- `cargo test -p codex-models-manager manager::tests`
- `cargo test -p codex-chatgpt`
- `cargo test -p codex-cloud-tasks`
- `cargo test -p codex-login agent_identity`
- `just fix -p codex-core -p codex-login -p codex-analytics -p
codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
codex-models-manager -p codex-chatgpt -p codex-model-provider -p
codex-mcp -p codex-core-skills`
- `just fix -p codex-app-server`
- `git diff --check`
This commit is contained in:
Adrian
2026-04-19 23:16:43 -07:00
committed by GitHub
parent 3c75f9b4dd
commit b44d2851cf
19 changed files with 1206 additions and 65 deletions
@@ -38,6 +38,55 @@ impl AuthProvider for BearerAuthProvider {
}
}
/// Auth provider for callers that already resolved the complete Authorization header value.
#[derive(Clone, Default)]
pub struct AuthorizationHeaderAuthProvider {
pub authorization_header_value: Option<String>,
pub account_id: Option<String>,
pub is_fedramp_account: bool,
}
impl AuthorizationHeaderAuthProvider {
pub fn new(authorization_header_value: Option<String>, account_id: Option<String>) -> Self {
Self {
authorization_header_value,
account_id,
is_fedramp_account: false,
}
}
pub fn for_test(authorization_header_value: Option<&str>, account_id: Option<&str>) -> Self {
Self {
authorization_header_value: authorization_header_value.map(str::to_string),
account_id: account_id.map(str::to_string),
is_fedramp_account: false,
}
}
pub fn with_fedramp_routing_header(mut self) -> Self {
self.is_fedramp_account = true;
self
}
}
impl AuthProvider for AuthorizationHeaderAuthProvider {
fn add_auth_headers(&self, headers: &mut HeaderMap) {
if let Some(authorization_header_value) = self.authorization_header_value.as_ref()
&& let Ok(header) = HeaderValue::from_str(authorization_header_value)
{
let _ = headers.insert(http::header::AUTHORIZATION, header);
}
if let Some(account_id) = self.account_id.as_ref()
&& let Ok(header) = HeaderValue::from_str(account_id)
{
let _ = headers.insert("ChatGPT-Account-ID", header);
}
if self.is_fedramp_account {
let _ = headers.insert("X-OpenAI-Fedramp", HeaderValue::from_static("true"));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -99,4 +148,54 @@ mod tests {
Some("true")
);
}
#[test]
fn authorization_header_auth_provider_supports_non_bearer_authorization_headers() {
let auth = AuthorizationHeaderAuthProvider::for_test(
Some("AgentAssertion opaque-token"),
Some("workspace-123"),
);
let mut headers = HeaderMap::new();
auth.add_auth_headers(&mut headers);
assert_eq!(
headers
.get(http::header::AUTHORIZATION)
.and_then(|value| value.to_str().ok()),
Some("AgentAssertion opaque-token")
);
assert_eq!(
headers
.get("ChatGPT-Account-ID")
.and_then(|value| value.to_str().ok()),
Some("workspace-123")
);
assert_eq!(
codex_api::auth_header_telemetry(&auth),
codex_api::AuthHeaderTelemetry {
attached: true,
name: Some("authorization"),
}
);
}
#[test]
fn authorization_header_auth_provider_adds_fedramp_routing_header_when_enabled() {
let auth = AuthorizationHeaderAuthProvider::for_test(
Some("AgentAssertion opaque-token"),
Some("workspace-123"),
)
.with_fedramp_routing_header();
let mut headers = HeaderMap::new();
auth.add_auth_headers(&mut headers);
assert_eq!(
headers
.get("X-OpenAI-Fedramp")
.and_then(|value| value.to_str().ok()),
Some("true")
);
}
}
+1
View File
@@ -2,6 +2,7 @@ mod auth;
mod bearer_auth_provider;
mod provider;
pub use bearer_auth_provider::AuthorizationHeaderAuthProvider;
pub use bearer_auth_provider::BearerAuthProvider;
pub use bearer_auth_provider::BearerAuthProvider as CoreAuthProvider;
pub use provider::ModelProvider;