Skip to content

feat(oauth): per-connection Claude OAuth raw passthrough (#13893) - #14053

Open
HouMinXi wants to merge 11 commits into
diegosouzapw:release/v3.8.52from
HouMinXi:feat/claude-oauth-raw-passthrough
Open

HouMinXi wants to merge 11 commits into
diegosouzapw:release/v3.8.52from
HouMinXi:feat/claude-oauth-raw-passthrough

Conversation

@HouMinXi

@HouMinXi HouMinXi commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Claude OAuth traffic that is not Claude Code currently still gets the CLI
cloak, fingerprint rewrite, and CCH body signing. Anthropic then 400s
sk-ant-oat tokens on extra-usage / cache-control for those clients
(#13893).

This adds a per-connection opt-in, providerSpecificData.rawPassthrough
(boolean, default false, no migration). When on, a Claude OAuth connection:

  • skips CLI cloak, static fingerprint headers, and signRequestBody
  • rewrites outgoing headers at the final merge (hop-by-hop + CLI leftovers)
  • keeps the gateway's own auth headers if the client Connection list
    nominates them
  • retries 400s without re-signing the body
  • resolves through the native claude executor instead of CLIProxyAPI / Dario

The dashboard toggle is limited to Claude OAuth. Save writes the top-level
boolean and drops nested passthrough.raw / passthrough.rawPassthrough
aliases so an import cannot re-arm the hatch after the operator turns it off.

API-key Claude connections keep the existing cloak even if the flag is set.

Related to #13893. #13893 stays open.

Test plan

  • node --import tsx/esm --test tests/unit/claude-oauth-raw-passthrough.test.ts (27/27)
  • adjacent cloak / thinking-guard / schema suites (93/93 combined)
  • npm run typecheck:core
  • i18n key completeness (65 locales)
  • maintainer: enable the toggle on one Claude OAuth connection, send a
    non-CLI Chat Completions request, confirm Anthropic 200 and that
    x-anthropic-billing / cch= are absent on the wire

⚠️ base-red inherited: #13866

HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 18, 2026
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi

Copy link
Copy Markdown
Contributor Author

CI on this branch matches the inherited set on pristine release/v3.8.51 (1603c86e06): API Route Typecheck, Docs Gates, Fast Quality Gates, and the four unit shards. Merge integrity, Vitest, and ESLint are green.

Unique tests: 50/50 locally (tests/unit/claude-oauth-raw-passthrough.test.ts + tests/unit/provider-specific-data-schema.test.ts). Unique files lint clean.

Default stays off (isConnectionRawPassthrough returns false on missing/malformed input). An explicit top-level false beats leftover nested aliases. Gateway auth headers cannot be nominated off the Connection list. No product change in this turn.

HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 18, 2026
…aves (diegosouzapw#13065)

PR diegosouzapw#14053 wired isClaudePassthrough into handleChatCore's three execute
sites. This branch already moved send into executeProviderRequest.ts, so
those sites would vanish on a later re-extract. Thread the marker from
translateAndDedup through sendDeps into the leaf executes and the leftover
credential-retry execute, and keep the proxy native fallback + dashboard
toggle.

Also drop client CLI-emulation headers (x-app / x-stainless-* /
anthropic-dangerous-direct-browser-access) during the final header merge.

Related to diegosouzapw#13893. diegosouzapw#13893 stays open.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi

Copy link
Copy Markdown
Contributor Author

Thinned this PR to the plan on issue 13893.

The hatch is now a small resolver next to resolveConnectionCacheOverride, a four-condition AND gate at the cloak block (isClaudePassthrough, genuine claude provider, OAuth token, flag), and the existing dashboard toggle. Nested passthrough.raw aliases no longer arm the hatch; save still writes a top-level boolean and strips them so an import cannot turn the switch back on.

Removed the standalone 177-line helper, the hop-by-hop / Connection header sanitizer, and the proxy-executor bypass. Those were extra surface. Fingerprint leftovers from buildHeaders (x-app, x-stainless-*, anthropic-dangerous-direct-browser-access) are still dropped on the raw path, which is the acceptance check that no Stainless header is injected.

On the two open questions: the flag hard-requires isClaudePassthrough (non-Claude-format clients keep the cloak), and the stored shape is a flat boolean.

Unique tests 20/20 after the cut. Head 9346e0c646.

@HouMinXi

Copy link
Copy Markdown
Contributor Author

Holding this behind the chatCore re-extract (#13065). Unique diff includes open-sse/handlers/chatCore.ts. Merging it first would add another hunk for the extraction to place. No maintainer review yet; nothing to adopt.

@HouMinXi
HouMinXi force-pushed the feat/claude-oauth-raw-passthrough branch from 9346e0c to 75161fa Compare September 18, 2026 11:08
HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 18, 2026
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi

Copy link
Copy Markdown
Contributor Author

Rebased onto current release/v3.8.51 after #14078.

The five locale files that conflicted kept the reviewed translations and the three raw-passthrough keys. Unit tests 20/20.

@HouMinXi
HouMinXi force-pushed the feat/claude-oauth-raw-passthrough branch from 75161fa to b99c611 Compare September 18, 2026 14:42
HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 18, 2026
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi

Copy link
Copy Markdown
Contributor Author

Rebased onto current release/v3.8.51 (36493a6270). Unique commits unchanged.

HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 20, 2026
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi
HouMinXi force-pushed the feat/claude-oauth-raw-passthrough branch from a53c8c9 to 1a1bd58 Compare September 20, 2026 13:09
HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 24, 2026
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi
HouMinXi force-pushed the feat/claude-oauth-raw-passthrough branch from bc07bdb to 13c74d5 Compare September 24, 2026 15:16
HouMinXi added a commit to HouMinXi/OmniRoute that referenced this pull request Sep 24, 2026
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi
HouMinXi force-pushed the feat/claude-oauth-raw-passthrough branch from 13c74d5 to 3406f5b Compare September 24, 2026 18:46
@diegosouzapw diegosouzapw added the deferred-v3.8.52 Grande demais / suspeito para o lote atual; precisa de sessão dedicada no ciclo v3.8.52 label Sep 25, 2026
@diegosouzapw diegosouzapw changed the title feat(oauth): per-connection Claude OAuth raw passthrough (#13893) [defer] feat(oauth): per-connection Claude OAuth raw passthrough (#13893) Sep 25, 2026
@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.51 to release/v3.8.52 September 29, 2026 11:24
@diegosouzapw

Copy link
Copy Markdown
Owner

Re-homed to release/v3.8.52: v3.8.51 entered its release freeze, so the branch now belongs to the release captain and development continues on the next cycle. Nothing is wrong with this PR — it just needed a live base. No action needed from you; CI will re-run against the new base.

Per-connection opt-in flag reader for the Claude OAuth raw passthrough
escape hatch. Precedence: explicit top-level boolean wins (including
false); nested legacy aliases passthrough.raw / passthrough.rawPassthrough
are consulted only when no explicit top-level boolean is present;
malformed input resolves to false. Object.hasOwn guards keep
prototype-inherited values from arming the hatch.

TDD: 7 cases (TC-04/04b/04c/04d, TC-07, TC-11/11b); injection-proven
(explicit-false override and hasOwn guard both go red when removed).
Forge LOCAL review: 3 consecutive clean rounds on the final diff.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…iegosouzapw#13893)

Server-side validation for the raw passthrough flag: boolean accepted,
non-boolean (string/number/null) rejected, consistent with the sibling
boolean keys that share the same guard shape. TC-15/TC-15b pin both
arms; injection-proven (removing the validator block turns TC-15b red).
The create-schema arm is covered transitively — sibling tests pin it
against the same shared validator, and an apiKey line in the excerpt
region gets credential-redacted by review tooling, breaking receipt
fidelity.

Forge LOCAL review: 3 consecutive clean rounds (threshold pinned to 3).

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…gosouzapw#13893)

Add applyFinalClaudeRawPassthroughHeaders: strips CLI-emulation markers
(x-app, anthropic-dangerous-direct-browser-access, x-stainless-*), RFC
hop-by-hop fields (static set + client Connection nominations), and merges
client business headers with case-variant dedupe. The gatewayAuthHeaders
set (authorization/x-api-key/x-goog-api-key/api-key/cookie) is anchored so
a client Connection header cannot strip gateway credentials (spec §3.3
rule 1, R5 hardening D-01).

Tests: TC-06/06b/12/14/14b/14c, 13/13 green. Defect injection: removing
the gatewayAuthHeaders guard turns TC-14b red; removing the case-variant
dedupe loop turns TC-12 and TC-06b red; both restored green.

Review: 5 local rounds on agnes-cn-3; all semantic findings dismissed or
adjudicated false with file:line evidence; sole recurring CONFIRMED is a
deterministic excerpt-fidelity defect of the review backend (filename
mistyped as claRawPassthrough.ts). Oscillation waiver approved by operator;
adjudication at .planning/reviews/task3-adjudication-20260917.md.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
… rawPassthrough (diegosouzapw#13893)

- ExecuteInput gains isClaudePassthrough; four-condition AND gate
  (native format + claude provider + official OAuth token + per-connection
  rawPassthrough flag) computes isRawPassthrough in execute()
- Rule 2: skip the cloak block (billing line, sentinel, tool remap,
  identity synthesis, CLI headers) when raw passthrough is active
- Rule 3: shouldFingerprint gated on !isRawPassthrough
- Rule 4: shouldSignBody unified across first send and all five retry
  branches, gated on !isRawPassthrough
- Rule 1: applyFinalClaudeRawPassthroughHeaders runs after
  mergeUpstreamExtraHeaders in raw mode
- Tests: TC-01/02/03/10 executor-level with stubbed upstream fetch;
  dual-arm injection proof (cloak guard removal reds TC-02+TC-10,
  sign guard removal reds TC-10); neighboring suites 170/170 green
- Forge LOCAL review: PASS, converged, 3 consecutive clean rounds

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…pass proxy executors (diegosouzapw#13893)

Raw passthrough must survive two extra hops after the executor gate:
retry bodies still skip CCH signing, and proxy executors (CLIProxyAPI /
Dario) must not swallow an OAuth connection that opted out of rewriting.

Pass the flag into all three handleChatCore execute() call sites and
short-circuit resolveExecutorWithProxy to the native claude executor
when the connection flag is on.

Tests: 23/23 (TC-05/08/09/13/15a/15b added). Adjacent suites 143/143.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…ons (diegosouzapw#13893)

Operators can opt a Claude OAuth connection out of CLI emulation from the
edit-connection modal. The toggle reuses isConnectionRawPassthrough so the
form matches executor semantics, and save strips nested passthrough.raw /
passthrough.rawPassthrough aliases so a later import cannot re-arm the hatch.

i18n: en + zh-CN copy from spec F-05; remaining locales filled from en.

Tests: 27/27 (TC-11 save round-trip). Forge LOCAL PASS, 3 clean rounds.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…osouzapw#13893)

User-facing opt-in on Claude OAuth connections. PR number will be filled
after GitHub assigns it.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Fill the PR number after GitHub assigned diegosouzapw#14053.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
Issue 13893 asked for a small resolver next to resolveConnectionCacheOverride,
a four-condition gate at the cloak block, and the existing dashboard toggle.
The standalone header sanitizer, Connection hop-by-hop merge, and proxy-executor
bypass were extra surface. Nested passthrough.raw aliases no longer arm the hatch.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…raw passthrough

In raw passthrough mode, omitting client headers left outbound requests with only
Authorization and Accept, dropping anthropic-version, anthropic-beta, user-agent,
and custom business headers. Anthropic rejects requests lacking anthropic-version
with 400 Bad Request.

Restore applyFinalClaudeRawPassthroughHeaders in cacheControlPolicy:
- Strips CLI-emulation markers (x-app, anthropic-dangerous-direct-browser-access, x-stainless-*)
- Strips RFC 9110 hop-by-hop headers (static list + client Connection nominations)
- Anchors gateway auth headers so client Connection nominations cannot strip them
- Merges client business headers with case-variant deduplication
- Ensures anthropic-version defaults to 2023-06-01 if omitted

Signed-off-by: Minxi Hou <houminxi@gmail.com>
…string header filtering

Add TC-14d to verify client-supplied anthropic-version is preserved rather than overwritten
by the default 2023-06-01 value, and TC-14e to assert non-string values in clientHeaders
are ignored during merge.

Signed-off-by: Minxi Hou <houminxi@gmail.com>
@HouMinXi
HouMinXi force-pushed the feat/claude-oauth-raw-passthrough branch from 3406f5b to 1398a21 Compare September 29, 2026 16:07
@diegosouzapw diegosouzapw removed the deferred-v3.8.52 Grande demais / suspeito para o lote atual; precisa de sessão dedicada no ciclo v3.8.52 label Oct 1, 2026
@diegosouzapw diegosouzapw changed the title [defer] feat(oauth): per-connection Claude OAuth raw passthrough (#13893) feat(oauth): per-connection Claude OAuth raw passthrough (#13893) Oct 1, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants