feat: add Codex OAuth provider with proxy-side credential injection#184
feat: add Codex OAuth provider with proxy-side credential injection#184alexeykazakov wants to merge 3 commits into
Conversation
Alternative to codeready-toolchain#183 that keeps real OAuth tokens on the proxy pod only. The gateway never sees refresh tokens or access tokens — it receives a synthetic unsigned JWT containing just the account_id for client-side provider routing. The proxy auto-refreshes tokens and injects Authorization, chatgpt-account-id, originator, and OpenAI-Beta headers. This addresses Codex as an LLM provider (via openai-chatgpt-responses wire format). Running the native Codex app-server harness is a separate problem with different security trade-offs — see docs/proposals/codex-harness-design.md for the early design direction. - New codexOAuth credential type and openai-oauth known provider - CodexOAuthInjector in internal/proxy with oauth2.TokenSource refresh - Controller parses auth.json, generates synthetic JWT, wires volume mounts and proxy routes - Design proposals and user-guide documentation Signed-off-by: Alexey Kazakov <alkazako@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (6)
✅ Files skipped from review due to trivial changes (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
WalkthroughThis PR introduces Codex OAuth credential support to OpenClaw, enabling ChatGPT Plus/Pro/Team authentication via refresh-token bootstrap and proxy-side OAuth token minting. New credential type ChangesCore Codex OAuth Feature Implementation
Design Proposals and User Documentation
Sequence Diagram(s)sequenceDiagram
participant User as User (CLI)
participant Secret as Kubernetes Secret
participant Controller as Claw Controller
participant Proxy as claw-proxy
participant ChatGPT as ChatGPT API (chatgpt.com)
User->>Secret: Create secret from auth.json (refresh_token, account_id)
User->>Controller: Apply Claw CR with codexOAuth credential
Controller->>Secret: Read auth.json
Controller->>Controller: Parse and validate auth.json
Controller->>Controller: Generate synthetic JWT (account_id claim only)
Controller->>Proxy: Configure route with codex_oauth injector
Controller->>Proxy: Mount auth.json secret to /etc/proxy/credentials/
Note over Proxy: At runtime...
Proxy->>Proxy: CodexOAuthInjector reads auth.json (once)
Proxy->>ChatGPT: POST token endpoint with refresh_token
ChatGPT-->>Proxy: access_token, expires_in
Proxy->>Proxy: Inject headers: Authorization (bearer), chatgpt-account-id, etc.
Proxy->>ChatGPT: Forward request with injected headers
ChatGPT-->>Proxy: Response
Proxy-->>User: Gateway receives response via injected route
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/proposals/codex-oauth-design.md`:
- Around line 45-79: The fenced block using triple backticks that contains the
ASCII diagram should include a language identifier (e.g., add "text" after the
opening ```) so the block becomes ```text ... ```, satisfying markdown linting
and Docs CI; locate the ASCII diagram block (the opening ``` and closing ```)
and add the identifier to the opening fence only.
In `@docs/proposals/codex-oauth-questions.md`:
- Around line 73-83: The two docs disagree about NetworkPolicy behavior for
codex OAuth: docs/proposals/codex-oauth-questions.md says the operator will
auto-add egress to auth.openai.com:443 and chatgpt.com:443 when a codexOAuth
credential is present, while docs/proposals/codex-oauth-design.md currently
states no NetworkPolicy changes are needed; pick one behavior and make both
files consistent by updating the text—for example, if you choose auto-add
behavior, change the design doc's NetworkPolicy statement to explicitly mention
auto-adding egress for auth.openai.com and chatgpt.com when codexOAuth
credentials exist (and mention the GCP oauth2 pattern), or if you choose no
changes, remove the auto-add claim from the questions doc and clarify operator
expectations; ensure references to "codexOAuth", "auth.openai.com", and
"chatgpt.com" match across both docs.
In `@internal/assets/manifests/claw/configmap.yaml`:
- Around line 620-647: The PLATFORM.md built-in provider list is missing
"openai-oauth", causing contradictory docs; update the provider list in
PLATFORM.md to add an entry for openai-oauth (include a one-line description
like "openai-oauth — OpenAI OAuth proxy for Codex/ChatGPT subscription models")
and ensure it matches the registry name used in code (knownProviders in
claw_providers.go) so the doc and the knownProviders mapping remain consistent;
adjust any examples or "known provider" enumeration text to include the exact
string "openai-oauth".
In `@internal/controller/claw_credentials.go`:
- Around line 251-258: The loop currently allows multiple secretRefs for
CredentialTypeCodexOAuth and overwrites rc.CodexOAuth; modify the block handling
CredentialTypeCodexOAuth (the branch that calls proxy.ParseCodexAuthJSON and
sets rc.CodexOAuth = &codexOAuthData{...}) to detect if rc.CodexOAuth is already
non-nil and, if so, append an error to errs (including cred.Name) and set
credFailed = true then continue, instead of parsing/overwriting; otherwise
proceed to parse and set rc.CodexOAuth as before.
In `@internal/proxy/injector_codex_oauth.go`:
- Around line 92-99: The code sets a synthetic 23h expiry on the seed
oauth2.Token (initialToken) which prevents cfg.TokenSource(ctx, initialToken)
from treating a stale access_token as expired; remove the forced assignment
initialToken.Expiry = time.Now().Add(23 * time.Hour) and instead leave
initialToken.Expiry unset (zero) or populate it only from a real expiry value if
available in auth, so TokenSource can correctly detect expiry and refresh;
update the logic around initialToken (used by cfg.TokenSource) accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Enterprise
Run ID: 63133b0b-7760-4294-acda-0fa022b0a460
📒 Files selected for processing (19)
api/v1alpha1/claw_types.goconfig/crd/bases/claw.sandbox.redhat.com_claws.yamldocs/proposals/codex-harness-design.mddocs/proposals/codex-oauth-design.mddocs/proposals/codex-oauth-questions.mddocs/user-guide.mdinternal/assets/manifests/claw/configmap.yamlinternal/controller/claw_configmap_test.gointernal/controller/claw_credentials.gointernal/controller/claw_credentials_test.gointernal/controller/claw_providers.gointernal/controller/claw_providers_test.gointernal/controller/claw_proxy.gointernal/controller/claw_proxy_test.gointernal/controller/claw_resource_controller.gointernal/proxy/config.gointernal/proxy/injector.gointernal/proxy/injector_codex_oauth.gointernal/proxy/injector_codex_oauth_test.go
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: E2E Tests
- GitHub Check: Unit Tests
🧰 Additional context used
📓 Path-based instructions (7)
**/*_test.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*_test.go: Userequireassertion from testify for fatal setup errors in tests, andassertfor value comparisons
Structure tests withTest*function names, uset.Run()for subtests,t.Cleanup()for cleanup, and implement table-driven test patterns
UsewaitFor(t, timeout, interval, condition, message)helper for async test assertions with 10s timeout and 250ms poll interval
Create separate test files per resource type (e.g.,claw_configmap_test.go,claw_credentials_test.go)
Files:
internal/controller/claw_providers_test.gointernal/controller/claw_proxy_test.gointernal/controller/claw_credentials_test.gointernal/proxy/injector_codex_oauth_test.gointernal/controller/claw_configmap_test.go
internal/controller/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Set owner references on all created resources via
controllerutil.SetControllerReference
Files:
internal/controller/claw_providers_test.gointernal/controller/claw_providers.gointernal/controller/claw_credentials.gointernal/controller/claw_proxy.gointernal/controller/claw_resource_controller.gointernal/controller/claw_proxy_test.gointernal/controller/claw_credentials_test.gointernal/controller/claw_configmap_test.go
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Include Go license header fromhack/boilerplate.go.txttemplate in all Go source files
Run golangci-lint viamake lintand usemake lint-fixfor auto-fixing, with.golangci.ymlconfiguration includinglllanddupllinters enabled
Usemake fmt(go fmt) andmake vet(go vet) for code formatting and vetting before committing
Files:
internal/controller/claw_providers_test.gointernal/proxy/config.gointernal/proxy/injector.gointernal/controller/claw_providers.goapi/v1alpha1/claw_types.gointernal/proxy/injector_codex_oauth.gointernal/controller/claw_credentials.gointernal/controller/claw_proxy.gointernal/controller/claw_resource_controller.gointernal/controller/claw_proxy_test.gointernal/controller/claw_credentials_test.gointernal/proxy/injector_codex_oauth_test.gointernal/controller/claw_configmap_test.go
**
⚙️ CodeRabbit configuration file
-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.
Files:
internal/controller/claw_providers_test.godocs/proposals/codex-harness-design.mddocs/proposals/codex-oauth-questions.mdinternal/proxy/config.gointernal/proxy/injector.gointernal/controller/claw_providers.goapi/v1alpha1/claw_types.godocs/user-guide.mdinternal/assets/manifests/claw/configmap.yamlinternal/proxy/injector_codex_oauth.goconfig/crd/bases/claw.sandbox.redhat.com_claws.yamlinternal/controller/claw_credentials.godocs/proposals/codex-oauth-design.mdinternal/controller/claw_proxy.gointernal/controller/claw_resource_controller.gointernal/controller/claw_proxy_test.gointernal/controller/claw_credentials_test.gointernal/proxy/injector_codex_oauth_test.gointernal/controller/claw_configmap_test.go
api/v1alpha1/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Modify API types in
api/v1alpha1/and runmake manifeststo regenerate CRD YAML inconfig/crd/bases/andmake generateto regenerate zz_generated.deepcopy.go
Files:
api/v1alpha1/claw_types.go
internal/assets/manifests/**/*.{yaml,yml}
📄 CodeRabbit inference engine (CLAUDE.md)
internal/assets/manifests/**/*.{yaml,yml}: Configure pod security with non-root user (uid 65532), restricted seccomp profile, and drop all Linux capabilities
SetreadOnlyRootFilesystem: trueon proxy andwait-for-proxycontainers, but not on init-config or gateway containers which require writable paths for Node.js and AI tools
Files:
internal/assets/manifests/claw/configmap.yaml
internal/controller/**/*_controller.go
📄 CodeRabbit inference engine (CLAUDE.md)
Apply RBAC authorization via
// +kubebuilder:rbac:...markers on reconciler methods in the controller
Files:
internal/controller/claw_resource_controller.go
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: codeready-toolchain/claw-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-01T02:00:32.268Z
Learning: Use the MITM proxy to inject credentials transparently, with known providers getting auto-inferred defaults from the centralized `knownProviders` registry in `claw_providers.go`
📚 Learning: 2026-06-01T02:00:32.268Z
Learnt from: CR
Repo: codeready-toolchain/claw-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-01T02:00:32.268Z
Learning: Use the MITM proxy to inject credentials transparently, with known providers getting auto-inferred defaults from the centralized `knownProviders` registry in `claw_providers.go`
Applied to files:
internal/controller/claw_providers.godocs/user-guide.mdinternal/assets/manifests/claw/configmap.yamlinternal/controller/claw_credentials.gointernal/controller/claw_proxy.gointernal/controller/claw_resource_controller.gointernal/controller/claw_proxy_test.gointernal/controller/claw_credentials_test.gointernal/controller/claw_configmap_test.go
📚 Learning: 2026-06-01T02:00:32.268Z
Learnt from: CR
Repo: codeready-toolchain/claw-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-01T02:00:32.268Z
Learning: Applies to api/v1alpha1/**/*.go : Modify API types in `api/v1alpha1/` and run `make manifests` to regenerate CRD YAML in `config/crd/bases/` and `make generate` to regenerate zz_generated.deepcopy.go
Applied to files:
api/v1alpha1/claw_types.go
📚 Learning: 2026-06-01T02:00:32.268Z
Learnt from: CR
Repo: codeready-toolchain/claw-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-01T02:00:32.268Z
Learning: Applies to **/*_test.go : Create separate test files per resource type (e.g., `claw_configmap_test.go`, `claw_credentials_test.go`)
Applied to files:
api/v1alpha1/claw_types.gointernal/controller/claw_proxy_test.gointernal/controller/claw_credentials_test.gointernal/proxy/injector_codex_oauth_test.gointernal/controller/claw_configmap_test.go
📚 Learning: 2026-05-27T15:29:16.197Z
Learnt from: alexeykazakov
Repo: codeready-toolchain/claw-operator PR: 149
File: internal/assets/manifests/claw/kustomization.yaml:19-19
Timestamp: 2026-05-27T15:29:16.197Z
Learning: In this repo’s claw Kustomize YAML manifests, when an image tag for ghcr.io (specifically ghcr.io/openclaw/openclaw) is being verified via `crane manifest` in an unauthenticated environment, a `not found` result can be caused by missing registry authentication (false positives). Do not flag the tag as missing based solely on that `crane manifest` output; require an authenticated verification step (e.g., GHCR login/token) or another independent check before concluding the tag is absent.
Applied to files:
internal/assets/manifests/claw/configmap.yaml
📚 Learning: 2026-06-01T02:00:32.268Z
Learnt from: CR
Repo: codeready-toolchain/claw-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-01T02:00:32.268Z
Learning: Manage Kubernetes resources via Kustomize with server-side apply in the unified `ClawResourceReconciler` controller managing all resources through two components: `claw/` and `claw-proxy/`
Applied to files:
internal/controller/claw_credentials.go
📚 Learning: 2026-06-01T02:00:32.268Z
Learnt from: CR
Repo: codeready-toolchain/claw-operator PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-06-01T02:00:32.268Z
Learning: Applies to **/*_test.go : Structure tests with `Test*` function names, use `t.Run()` for subtests, `t.Cleanup()` for cleanup, and implement table-driven test patterns
Applied to files:
internal/controller/claw_credentials_test.go
🪛 LanguageTool
docs/proposals/codex-oauth-questions.md
[grammar] ~77-~77: Ensure spelling is correct
Context: ...Option A: Auto-add auth.openai.com as a builtin passthrough when codexOAuth is present ...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.22.1)
docs/proposals/codex-oauth-design.md
[warning] 45-45: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (2)
docs/proposals/codex-harness-design.md (1)
1-71: LGTM!docs/user-guide.md (1)
13-13: LGTM!Also applies to: 117-156
- Add language identifier to ASCII diagram code fence - Fix NP docs inconsistency: questions doc now matches design doc (no NP changes needed — proxy egress already allows TCP/443) - Add openai-oauth to PLATFORM.md known provider list - Remove synthetic 23h expiry on seed token — let TokenSource refresh immediately rather than guessing expiry Signed-off-by: Alexey Kazakov <alkazako@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #184 +/- ##
==========================================
+ Coverage 79.28% 79.50% +0.22%
==========================================
Files 31 32 +1
Lines 4199 4313 +114
==========================================
+ Hits 3329 3429 +100
- Misses 551 564 +13
- Partials 319 320 +1
🚀 New features to boost your workflow:
|
Alternative to #183 that keeps real OAuth tokens on the proxy pod only. The gateway never sees refresh tokens or access tokens — it receives a synthetic unsigned JWT containing just the account_id for client-side provider routing. The proxy auto-refreshes tokens and injects Authorization, chatgpt-account-id, originator, and OpenAI-Beta headers.
This addresses Codex as an LLM provider (via openai-chatgpt-responses wire format). Running the native Codex app-server harness is a separate problem with different security trade-offs — see
docs/proposals/codex-harness-design.md for the early design direction.
Summary by CodeRabbit
Release Notes
New Features
openai-oauthprovider.Documentation
Tests