feat: load AgentIdentity from JWT login/env (#18904)

## Summary

This PR lets programmatic AgentIdentity users provide one token through
either stdin login or environment auth.

`codex login --with-agent-identity` reads an Agent Identity JWT from
stdin, validates that it has the required claims, and stores that token
as the `agent_identity` value in `auth.json`. The file format is
token-only; the decoded account and key fields are runtime state, not
hand-authored auth.json fields.

The Agent Identity JWT claim shape and decoder live in
`codex-agent-identity`; `codex-login` only owns env/storage precedence
and conversion into `CodexAuth::AgentIdentity`.

When env auth is enabled, `CODEX_AGENT_IDENTITY` can provide the same
JWT without writing auth state to disk. `CODEX_API_KEY` still wins if
both env vars are set.

Reference old stack: https://github.com/openai/codex/pull/17387/changes
Reference JWT/env stack: https://github.com/openai/codex/pull/18176

## Stack

1. https://github.com/openai/codex/pull/18757: full revert
2. https://github.com/openai/codex/pull/18871: isolated Agent Identity
crate
3. https://github.com/openai/codex/pull/18785: explicit AgentIdentity
auth mode and startup task allocation
4. https://github.com/openai/codex/pull/18811: migrate Codex backend
auth callsites through AuthProvider
5. This PR: accept AgentIdentity JWTs through login/env

## Testing

Tests: targeted login and Agent Identity crate tests, CLI checks, scoped
formatter/linter cleanup, and CI.

---------

Co-authored-by: Shijie Rao <shijie.rao@openai.com>
This commit is contained in:
efrazer-oai
2026-04-26 12:49:54 -07:00
committed by GitHub
Unverified
parent ac2bffa443
commit fed0a8f4fa
14 changed files with 587 additions and 49 deletions
+18 -1
View File
@@ -10,8 +10,10 @@ use codex_chatgpt::apply_command::run_apply_command;
use codex_cli::LandlockCommand;
use codex_cli::SeatbeltCommand;
use codex_cli::WindowsCommand;
use codex_cli::read_agent_identity_from_stdin;
use codex_cli::read_api_key_from_stdin;
use codex_cli::run_login_status;
use codex_cli::run_login_with_agent_identity;
use codex_cli::run_login_with_api_key;
use codex_cli::run_login_with_chatgpt;
use codex_cli::run_login_with_device_code;
@@ -359,6 +361,12 @@ struct LoginCommand {
)]
with_api_key: bool,
#[arg(
long = "with-agent-identity",
help = "Read the experimental Agent Identity token from stdin (e.g. `printenv CODEX_AGENT_IDENTITY | codex login --with-agent-identity`)"
)]
with_agent_identity: bool,
#[arg(
long = "api-key",
num_args = 0..=1,
@@ -940,7 +948,12 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
run_login_status(login_cli.config_overrides).await;
}
None => {
if login_cli.use_device_code {
if login_cli.with_api_key && login_cli.with_agent_identity {
eprintln!(
"Choose one login credential source: --with-api-key or --with-agent-identity."
);
std::process::exit(1);
} else if login_cli.use_device_code {
run_login_with_device_code(
login_cli.config_overrides,
login_cli.issuer_base_url,
@@ -955,6 +968,10 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
} else if login_cli.with_api_key {
let api_key = read_api_key_from_stdin();
run_login_with_api_key(login_cli.config_overrides, api_key).await;
} else if login_cli.with_agent_identity {
let agent_identity = read_agent_identity_from_stdin();
run_login_with_agent_identity(login_cli.config_overrides, agent_identity)
.await;
} else {
run_login_with_chatgpt(login_cli.config_overrides).await;
}