Skip to content

feat(sse): refresh Claude OAuth wire image to claude-cli/2.1.131#2011

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.0from
Tentoxa:feat/claude-oauth-cloak-refresh
May 7, 2026
Merged

feat(sse): refresh Claude OAuth wire image to claude-cli/2.1.131#2011
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.0from
Tentoxa:feat/claude-oauth-cloak-refresh

Conversation

@Tentoxa
Copy link
Copy Markdown
Contributor

@Tentoxa Tentoxa commented May 6, 2026

Summary

Refresh the native Claude OAuth wire image to match claude-cli/2.1.131, and
extend the cloak so it fires on any sk-ant-oat token regardless of upstream
client. This resolves the upstream 429s seen when routing Claude Max OAuth
credentials through non-CLI clients (ForgeCode, Cursor, Cline).

Highlights:

  • Per-OAuth-account cliUserID / accountUUID / sessionId generated and
    persisted via mapTokens; preserved across token refresh.
  • postExchange calls /api/claude_cli/bootstrap to capture real account
    metadata at provisioning time (best-effort — failure does not block OAuth).
  • Adaptive anthropic-beta selection per request shape (probe / structured
    / full-agent) replaces the previous static set.
  • Per-request behavior overrides via x-omniroute-effort and
    x-omniroute-thinking headers; auto-mirror context_management whenever
    thinking is set.
  • Migrate deprecated top-level output_formatoutput_config.format in
    chatCore to fix the 400 some clients trigger.
  • Dashboard "Test Model" for OAuth Claude now routes through the executor
    instead of duplicating cloak logic — single source of truth.

Related Issues

Validation

  • npm run lint
  • npm run test:unit
  • npm run test:coverage
  • Coverage is still >= 60% for statements, lines, functions, and branches
  • SonarQube PR analysis is green or any remaining issues are explicitly documented below

Tests Added Or Updated

No new test files. Existing Claude unit suites all pass (69 tests, 12 suites).

Coverage Notes

Touched files in open-sse/ and src/lib/ are covered by existing
claude-* suites. The new helper module open-sse/executors/claudeIdentity.ts
is exercised transitively by claude-code-parity and
anthropic-cache-fingerprint. Happy to add focused unit tests for
selectBetaFlags / parseUpstreamMetadataUserId / resolveCliUserID if
reviewers want them.

Reviewer Notes

  • Cloak gate widened: now fires on sk-ant-oat token detection in
    addition to isClaudeCodeClient. API-key Claude (sk-ant-api) and all
    other providers are unaffected.
  • Existing OAuth connections keep working but only get persistent
    accountUUID / cliUserID after re-auth. Pre-existing accounts use a
    background bootstrap fetch + in-memory cache as a stopgap.
  • New opt-in headers x-omniroute-effort and x-omniroute-thinking documented
    inline in base.ts; default is pure passthrough.

- Per-OAuth-account identity (cliUserID, accountUUID, sessionId) generated
  with the same crypto.randomBytes(32) algorithm real CLI uses, persisted
  to providerSpecificData via mapTokens. Token refresh preserves the
  device id (the device is the machine, not the credential).
- postExchange fetches /api/claude_cli/bootstrap to populate accountUUID,
  email, and organization metadata at OAuth provisioning. Best-effort:
  bootstrap failures do not block OAuth.
- Adaptive anthropic-beta selection per request shape (probe / structured
  / full-agent) replaces the static set, matching what real CLI emits
  conditionally on body shape.
- Cloak gate widened to cover OAuth tokens (sk-ant-oat) regardless of
  upstream client, so non-CLI clients (ForgeCode, Cursor, Cline) routing
  through OmniRoute on a Claude Max OAuth credential still emit
  CLI-shaped traffic on the user:sessions:claude_code scope.
- Per-request behavior overrides via x-omniroute-effort and
  x-omniroute-thinking custom headers. Auto-mirror context_management
  whenever thinking is set but context_management isn't, matching the
  real-CLI invariant.
- output_format -> output_config.format migration in chatCore for clients
  still emitting the deprecated top-level field (Anthropic now returns 400
  on it).
- Validation routes Claude OAuth probes through getExecutor("claude")
  instead of duplicating the cloak — single source of truth.
- Header order in cliFingerprints matches real-CLI emission (Title-Case
  alphabetical, then lowercase alphabetical, then transport).
- Tool cache_control kept on the translator path; cloak strips it for
  Claude OAuth requests (real CLI never sets cache_control on tools).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Tentoxa Tentoxa requested a review from diegosouzapw as a code owner May 6, 2026 18:50
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a comprehensive "cloak" for Claude OAuth traffic to ensure it mimics the official Anthropic CLI, addressing issues with rate limiting and prompt cache affinity. Key changes include the introduction of claudeIdentity.ts for session and device ID management, updating the pinned CLI version to 2.1.131, and refining header and body field ordering to match official fingerprints. Feedback focuses on ensuring fingerprinting is applied to all OAuth requests, managing memory for identity caches, adding timeouts to background fetch operations, and centralizing version constants for better maintainability.

Comment thread open-sse/executors/base.ts Outdated
Comment on lines +725 to +727
const shouldFingerprint =
isCliCompatEnabled(this.provider) ||
(this.provider === "claude" && isClaudeCodeClient);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fingerprinting logic (which controls header and body field ordering) should be applied to all Claude OAuth requests to ensure they are indistinguishable from official CLI traffic. Currently, this is only triggered if isClaudeCodeClient is true, but the 'cloak' logic earlier in this method (lines 519-718) correctly applies to any hasClaudeOAuthToken. Missing this ordering on non-CLI clients using OAuth tokens increases the risk of 429 errors on the claude_code scope.

Suggested change
const shouldFingerprint =
isCliCompatEnabled(this.provider) ||
(this.provider === "claude" && isClaudeCodeClient);
const shouldFingerprint =
isCliCompatEnabled(this.provider) ||
(this.provider === "claude" && (isClaudeCodeClient || hasClaudeOAuthToken));


// ---------- Session ID (per OAuth account, process lifetime) -------------

const sessionCache = new Map<string, string>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The sessionCache, lazyCliUserIDCache, and accountUuidCache maps are unbounded and will grow indefinitely as new OAuth tokens or seeds are processed. In a long-running server environment, this constitutes a memory leak. Consider using an LRU cache or a Map with a maximum size limit to prune old entries.

Comment on lines +128 to +135
const res = await fetch("https://api.anthropic.com/api/claude_cli/bootstrap", {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"User-Agent": `claude-cli/${CLAUDE_CODE_VERSION} (external, cli)`,
"anthropic-beta": "oauth-2025-04-20",
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The fetch call to the bootstrap endpoint lacks a timeout. While this is a background operation, a hanging network request can indefinitely consume a socket and memory. It is recommended to use an AbortSignal with a reasonable timeout (e.g., 10 seconds).

Comment on lines +7 to +14
const res = await fetch("https://api.anthropic.com/api/claude_cli/bootstrap", {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"User-Agent": "claude-cli/2.1.131 (external, cli)",
"anthropic-beta": "oauth-2025-04-20",
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The fetch call to the bootstrap endpoint lacks a timeout. Network issues or slow upstream responses could cause this call to hang, potentially delaying the completion of the OAuth flow. Consider adding a timeout via AbortController.

Comment thread src/lib/oauth/providers/claude.ts Outdated
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"User-Agent": "claude-cli/2.1.131 (external, cli)",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The User-Agent string is hardcoded here. To ensure consistency across the codebase and simplify future version bumps, consider importing CLAUDE_CODE_VERSION from @omniroute/open-sse/executors/claudeIdentity.ts and using it in a template literal.

  - Widen `shouldFingerprint` gate to also fire on `hasClaudeOAuthToken`,
    matching the cloak gate. Fixes ordering on non-CLI OAuth clients.
  - Bound identity caches (sessionCache, lazyCliUserIDCache,
    accountUuidCache) with FIFO eviction at 10k entries to prevent
    unbounded growth in long-running multi-tenant deployments.
  - Add 10s AbortController timeout to both bootstrap fetches
    (claudeIdentity background path and oauth/providers/claude
    provisioning path) so a hung upstream cannot leak sockets or stall
    OAuth completion.
  - Replace hardcoded "claude-cli/2.1.131" UA in oauth/providers/claude.ts
    with the CLAUDE_CODE_VERSION constant from claudeIdentity.ts so future
    version bumps are a single-line change.
@Tentoxa
Copy link
Copy Markdown
Contributor Author

Tentoxa commented May 6, 2026

Addressed all issues found by Gemini in a07657b

@diegosouzapw
Copy link
Copy Markdown
Owner

Thank you @Tentoxa for your contribution! This has been reviewed and is now merged into release/v3.8.0 for the upcoming release. Amazing work!

@diegosouzapw diegosouzapw merged commit eb2579e into diegosouzapw:release/v3.8.0 May 7, 2026
2 checks passed
@diegosouzapw diegosouzapw mentioned this pull request May 10, 2026
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