Skip to content

fix(auth): key admission-bearer substitution on transport, not provider name - #2169

Merged
lidge-jun merged 1 commit into
devfrom
codex/fix-admission-bearer-transport
Aug 20, 2026
Merged

fix(auth): key admission-bearer substitution on transport, not provider name#2169
lidge-jun merged 1 commit into
devfrom
codex/fix-admission-bearer-transport

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Summary

An admission bearer is one of our secrets. The contract in src/codex/auth-context.ts is explicit: it is either replaced with the stored main credential, or the request fails before any I/O. It must never reach an upstream.

Found while auditing origin/main..origin/dev for release safety.

The gap. #2132 correctly stopped requiring a ChatGPT credential for routed providers, but it asked the question by provider namecodexAccountMode comes from providerCodexAccountMode, which special-cases the id openai. The passthrough adapter asks the same question by transport: isCanonicalOpenAiForwardProvider reads adapter, auth mode, and base URL (openai-responses.ts:1451), and when it says yes it copies the caller's Authorization header upstream.

Those two predicates disagree for one configuration: a provider row the operator named anything other than openai, pointed at the canonical ChatGPT backend with authMode: "forward". It satisfies the adapter and fails the name check, so substitution was skipped and the adapter forwarded our admission secret.

Reproduced through the real adapter, no mocking of the code under test:

URL:  https://chatgpt.com/backend-api/codex/responses
AUTH: Bearer ocx_data_this_is_our_proxy_key
LEAKED: true

The fix. Substitution now consults the same authority the adapter does, so the transport that actually carries the header is what decides. A key-authenticated routed provider is still not canonical-forward, so the install #2132 was filed for keeps working without a ChatGPT login — that behavior is pinned by the three pre-existing tests, which stay green.

Two predicates answering one security question is the underlying bug; this leaves one authority.

Verification

  • bun run typecheck — clean.
  • bun test --isolate tests/bearer-admission-routed-provider.test.ts tests/server-auth.test.ts tests/codex-auth-context.test.ts143 pass / 0 fail.
  • RED-first. Reverting only src/server/responses/core.ts fails exactly the two new tests (3 pass / 2 fail). The three that stay green are [Bug] non-OpenAI providers unusable without a ChatGPT login after v2.23.0 #2132's behaviors, which is the evidence this fix did not undo the thing it builds on.

The new tests assert the invariant — an admission bearer never reaches the wire for a canonical transport, whatever the row is named — rather than the implementation detail, so a future refactor cannot satisfy them by accident.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

This is the security-boundary surface MAINTAINERS.md reserves for explicit review. The change is strictly narrowing: it adds a condition under which substitution happens, and removes none.

Summary by CodeRabbit

  • Bug Fixes
    • Prevented admission credentials from being forwarded to canonical ChatGPT services through custom-named providers.
    • Requests now fail safely when no stored credential is available.
    • When available, the stored main credential is used instead of the caller’s admission credential.

…er name

An admission bearer is one of OUR secrets. The contract in
src/codex/auth-context.ts is that it is either replaced with the stored
main credential or the request fails before any I/O -- it must never
reach an upstream.

#2132 correctly stopped requiring a ChatGPT credential for routed
providers, but it asked the question by provider NAME:
codexAccountMode comes from providerCodexAccountMode, which special-cases
the id "openai". The passthrough adapter asks the same question by
TRANSPORT: isCanonicalOpenAiForwardProvider reads adapter, auth mode, and
base URL, and when it says yes it copies the caller's Authorization
header upstream.

A provider row named anything else, pointed at the canonical ChatGPT
backend with authMode "forward", satisfies the adapter and fails the
name check. Substitution was skipped and the admission secret was
forwarded to ChatGPT. Reproduced through the real adapter:

  URL:  https://chatgpt.com/backend-api/codex/responses
  AUTH: Bearer ocx_data_this_is_our_proxy_key

Substitution now consults the same authority the adapter does, so the
transport that actually carries the header is what decides. A
key-authenticated routed provider is still not canonical-forward, so the
install #2132 was filed for keeps working without a ChatGPT login.

The two new tests assert the invariant rather than the implementation:
an admission bearer never reaches the wire for a canonical transport,
whatever the row is called.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 20, 2026 03:54
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 20, 2026
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 98b1d38a-9ac2-43c5-a85d-a87905b171cc

📥 Commits

Reviewing files that changed from the base of the PR and between a584890 and b8710a7.

📒 Files selected for processing (2)
  • src/server/responses/core.ts
  • tests/bearer-admission-routed-provider.test.ts

📝 Walkthrough

Walkthrough

The response authentication path now substitutes the stored main credential for custom-named providers that use canonical OpenAI forward routing. Tests verify fail-closed behavior without stored credentials and correct credential forwarding when one exists.

Changes

Canonical forward credential handling

Layer / File(s) Summary
Credential substitution logic
src/server/responses/core.ts
resolveResponsesCodexAuth now treats canonical OpenAI forward providers as eligible for stored main credential substitution.
Admission bearer regression coverage
tests/bearer-admission-routed-provider.test.ts
Adds tests for a custom-named canonical provider. Requests fail closed without a stored credential and use the stored credential when available.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • lidge-jun/opencodex#2132 — The change prevents admission bearer secrets from reaching canonical ChatGPT transports.

Possibly related PRs

  • lidge-jun/opencodex#2137 — Both changes update bearer-admission credential substitution and related regression tests.

Suggested labels: review-ready

Suggested reviewers: ingwannu

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-admission-bearer-transport

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lidge-jun
lidge-jun merged commit 00e333d into dev Aug 20, 2026
22 of 23 checks passed
@lidge-jun
lidge-jun deleted the codex/fix-admission-bearer-transport branch August 20, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant