feat: verify agent identity JWTs with JWKS#19764
feat: verify agent identity JWTs with JWKS#19764efrazer-oai wants to merge 2 commits intodev/efrazer/agent-identity-eager-runtimefrom
Conversation
43b69d7 to
52aeb29
Compare
|
@codex review |
ee7d9b3 to
d69ac63
Compare
There was a problem hiding this comment.
💡 Codex Review
codex/codex-rs/agent-identity/src/lib.rs
Lines 347 to 348 in 52aeb29
normalize_chatgpt_base_url removes any trailing /codex. With a configured base like http://host/api/codex, this truncates to http://host/api; then JWKS URL construction becomes .../api/api/codex/agent-identities/jwks. That breaks JWKS fetch and causes valid AgentIdentity JWTs to fail verification on direct Codex API deployments.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /*enable_codex_api_key_env*/ true, | ||
| config.auth_credentials_store_mode, | ||
| )? | ||
| /*chatgpt_base_url*/ None, |
There was a problem hiding this comment.
Use configured base URL in login restriction auth load
enforce_login_restrictions always calls load_auth(..., None) for chatgpt_base_url. After this change, AgentIdentity auth verification fetches JWKS using that value, so this path falls back to https://chatgpt.com/backend-api instead of the runtime-configured backend. In non-default/staging/self-hosted setups, restriction checks can fail and abort startup even with valid credentials.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Added this, also simplified he normalize_base_url thing it shouldn't really have existed in the way it did.
64a404b to
c21a966
Compare
4a656a8 to
8c73abd
Compare
|
@codex review |
8c73abd to
464097e
Compare
|
@codex review |
464097e to
f085622
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f085622f31
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| base_url = stripped.to_string(); | ||
| } | ||
| if (base_url.starts_with("https://chatgpt.com") |
There was a problem hiding this comment.
Preserve Codex API base URLs on ChatGPT hosts
Do not append /backend-api when the configured base URL already targets /api/codex. In normalize_chatgpt_base_url, a value like https://chatgpt.com/api/codex is rewritten to .../api/codex/backend-api, which makes agent_identity_jwks_url build a WHAM JWKS path that does not match Codex-API style routing and causes JWT verification to fail.
Useful? React with 👍 / 👎.
f085622 to
ec92ff1
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec92ff1eae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let decoding_key = DecodingKey::from_jwk(jwk).context("failed to build JWT decoding key")?; | ||
| let mut validation = Validation::new(Algorithm::RS256); | ||
| validation.set_audience(&[AGENT_IDENTITY_JWT_AUDIENCE]); | ||
| validation.set_issuer(&[AGENT_IDENTITY_JWT_ISSUER]); |
There was a problem hiding this comment.
Validate issuer against the configured backend, not a fixed host
decode_agent_identity_jwt verifies signatures using JWKS fetched from the caller-provided chatgpt_base_url, but still hard-codes issuer to https://chatgpt.com/.... In non-prod/custom deployments (e.g. localhost or staging), correctly signed tokens from that backend can be rejected solely due to issuer mismatch, breaking AgentIdentity auth load/login flows.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
codex-backend currently always sets issuer to chatgpt.com regardless of the deployment, this should be fine.
## Summary Auth loading used to expose synchronous construction helpers in several places even though some auth sources now need async work. This PR makes the auth-loading surface async and updates the callers to await it. This is intentionally only plumbing. It does not change how AgentIdentity tokens are decoded, how task runtime ids are allocated, or how JWT signatures are verified. ## Stack 1. **This PR:** [refactor: make auth loading async](#19762) 2. [refactor: load AgentIdentity runtime eagerly](#19763) 3. [feat: verify AgentIdentity JWTs with JWKS](#19764) ## Important call sites | Area | Change | | --- | --- | | `codex-login` auth loading | `CodexAuth` and `AuthManager` construction paths now await auth loading. | | app-server startup | Auth manager construction is awaited during initialization. | | CLI/TUI/exec/MCP/chatgpt callers | Existing auth-loading calls now await the same behavior. | | cloud requirements storage loader | The loader becomes async so it can share the same auth construction path. | | auth tests | Tests that load auth now run in async contexts. | ## Testing Tests: targeted Rust auth test compilation, formatter, scoped Clippy fix, and Bazel lock check.
dfb08d0 to
381d0dc
Compare
381d0dc to
248b00b
Compare
ec92ff1 to
415cb77
Compare
3d6fc02 to
da21113
Compare
| .find(&kid) | ||
| .with_context(|| format!("agent identity JWT kid {kid} is not trusted"))?; | ||
| let decoding_key = DecodingKey::from_jwk(jwk).context("failed to build JWT decoding key")?; | ||
| let mut validation = Validation::new(Algorithm::RS256); |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da211139fe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| pub fn agent_identity_jwks_url(chatgpt_base_url: &str) -> String { | ||
| let trimmed = chatgpt_base_url.trim_end_matches('/'); | ||
| format!("{trimmed}/wham/agent-identities/jwks") |
There was a problem hiding this comment.
Build JWKS URL for /api/codex bases correctly
agent_identity_jwks_url always appends /wham/agent-identities/jwks to the configured base. With a Codex-API base like https://.../api/codex, this produces .../api/codex/wham/agent-identities/jwks, but JWKS is served at .../api/codex/agent-identities/jwks. AgentIdentity JWT verification will fail in Codex-API style deployments, breaking auth load/login.
Useful? React with 👍 / 👎.
Summary
AgentIdentity auth could previously read the JWT payload but did not verify that the token was signed by the backend. This PR verifies AgentIdentity JWTs by fetching the backend JWKS and passing the matching key into the existing
jsonwebtokenverification path.The existing login/storage write path still performs a payload-shape check before saving the token. The actual auth load path fetches JWKS, verifies issuer/audience/signature/expiry, then constructs the same AgentIdentity auth record from the verified claims.
Stack
Important call sites
codex-agent-identityjsonwebtokenverification usingkid.codex-loginauth loadAgentIdentityAuth.codex login statusDesign decisions
JWT verification stays in
codex-agent-identity, next to the claim type and existing JWT parsing helper. The implementation delegates signature and claim validation tojsonwebtoken; this code only fetches the JWKS, selects the trusted key bykid, and supplies issuer/audience requirements.The JWKS URL follows existing Codex backend path style:
/backend-apibases use/wham/agent-identities/jwks, while direct Codex API bases use/api/codex/agent-identities/jwks.Testing
Tests: targeted AgentIdentity tests, targeted AgentIdentity env-auth test, targeted Rust checks for touched crates, formatter, scoped Clippy fix, and Bazel lock check.