feat(auth): accept IdP-issued OIDC bearer tokens on the API#35
Merged
Conversation
…audiences Groundwork for accepting OIDC provider-issued access tokens as API bearer credentials (resource-server support), so downstream apps like Shisho can forward their users' IdP tokens to Codex. - Add `accepted_audiences` to OidcProviderConfig: audiences accepted on API bearer tokens from a provider, defaulting to the provider's own client_id. Configurable via YAML or the ..._ACCEPTED_AUDIENCES env override (comma-separated). - New codex_services::idp_bearer::IdpBearerValidator: validates RS256/ ES256 bearer JWTs against the matching provider's JWKS, selected by an unverified `iss` peek and re-verified during validation. Enforces issuer (trailing-slash tolerant), audience against the accepted list, and exp/nbf with 30s clock-skew leeway. `iss`/`aud` are required claims so tokens missing them fail closed. - HS256 and alg=none are rejected outright on this path; symmetric algorithms stay reserved for Codex's local session JWTs, closing the key-confusion attack. - Per-provider JWKS cache: lazy fetch from the discovery document, 1h refresh, stale-serve on refresh failure, and a rate-limited refetch on unknown `kid` so key rotation works without a restart. HTTP client has redirects disabled and a 10s timeout. - Error enum distinguishes every rejection cause for precise auth logs (never echoing token material) and separates IdP outages from token rejections. Validation is not wired into the auth extractor yet; this is the self-contained service layer. Unit and integration tests added, the latter against a local axum IdP stub with static RSA test fixtures.
Wire the IdP bearer validator into the auth extractor, making Codex a proper OAuth2 resource server: provider-issued RS256/ES256 access tokens now authenticate API requests for users who have linked that identity via web SSO. This unblocks downstream services (e.g. Shisho's passthrough strategy) that forward their callers' IdP tokens to Codex. - Route bearer tokens by JWT header algorithm in extract_from_jwt: HS256 session tokens keep the existing local-secret path verbatim; RS256/ES256 divert to the IdP validator only when one is configured. Without a validator (OIDC disabled or no providers), behavior is unchanged, including error messages. - Resolve validated (provider, subject) identities through oidc_connections; no auto-provisioning. An unlinked identity gets a 401 telling the user to sign into Codex via SSO once. Inactive users are rejected identically to the local path. - Add AppState.idp_bearer (IdpBearerAuth): the validator bundled with a TTL'd (provider, subject) -> user_id cache so hot endpoints don't hit the connection table per request; user data freshness stays governed by the existing user auth cache, which both paths now share through a common resolve_user_context helper. - Add AuthMethod::OidcBearer for audit visibility. - Token rejections return a uniform 401 with the precise cause logged (never echoing token material); IdP-side failures (discovery/JWKS unreachable) return 503 instead of 401 so outages don't read as bad credentials. Covers both AuthContext and FlexibleAuthContext extractors. Unit tests for the subject cache and dispatch helper, plus API integration tests against a local IdP stub (linked/unlinked/tampered/inactive/disabled and HS256/Basic-auth regression with the validator configured).
Operator-facing documentation for the resource-server support added in the previous commits, so the feature is configurable without reading source. - New "API Bearer Tokens (Resource Server)" section in the OIDC docs: how validation works (algorithm allowlist, issuer-based provider selection, JWKS verification with automatic key-rotation pickup, audience and expiry enforcement), the sign-in-via-SSO-once linking requirement and why there is no auto-provisioning, and audience rules with a worked accepted_audiences example. - Troubleshooting table mapping each server-logged rejection reason to its likely cause and fix, including the 503 IdP-unreachable case. - accepted_audiences added to the provider settings reference and the environment variable override examples. - Commented accepted_audiences examples in the shipped sqlite, docker, and kubernetes config templates.
Deploying codex with
|
| Latest commit: |
71e4802
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://992e9091.codex-asm.pages.dev |
| Branch Preview URL: | https://idp-bearer-token.codex-asm.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Codex API now accepts access tokens issued by a configured OIDC identity provider (e.g. Authentik) as
Authorization: Bearercredentials, in addition to Codex's own session tokens. A valid IdP token is resolved to the local user who previously linked that identity by signing into Codex web via SSO. This makes Codex a proper OAuth2 resource server on top of its existing OIDC login support.Motivation
Until now, the API bearer path only accepted Codex's own session tokens, so any IdP-issued token was rejected with "Invalid JWT token". This broke every downstream system that forwards a caller's IdP token to Codex, concretely Shisho's passthrough authentication (both its web SSO sign-in and the claude.ai MCP connector fail on the Codex
/api/v1/auth/mecheck). Codex is the identity authority for that ecosystem, so the resource-server half belongs here rather than in downstream workarounds.Changes
Authorization: Bearernow accepts tokens issued by any configured OIDC provider. The token's issuer, audience, signature, and expiry are verified against the provider, and signing-key rotation at the IdP is picked up automatically without a restart.accepted_audienceslist controls which audiences are accepted on API bearer tokens, defaulting to the provider'sclient_id; settable via config file or the matching..._ACCEPTED_AUDIENCESenvironment variable. Shipped example configs include a commented sample.Notes