Skip to content

dont use forward API key in case of claude code request#1585

Merged
akshaydeo merged 1 commit intomainfrom
02-09-dont_use_forward_api_key_in_case_of_claude_code_request
Feb 9, 2026
Merged

dont use forward API key in case of claude code request#1585
akshaydeo merged 1 commit intomainfrom
02-09-dont_use_forward_api_key_in_case_of_claude_code_request

Conversation

@akshaydeo
Copy link
Contributor

@akshaydeo akshaydeo commented Feb 9, 2026

Summary

Improve Claude Code (claude-cli) integration by refactoring the detection logic and fixing authentication handling for passthrough requests.

Issues

Closes #1551

Changes

  • Extracted the Claude Code detection logic into a reusable IsClaudeCodeRequest utility function
  • Fixed authentication header handling for Claude Code requests in passthrough mode
  • Simplified code by using the new utility function across multiple files
  • Improved WebSearch tool argument handling for Claude Code requests

Type of change

  • Bug fix
  • Refactor
  • Feature
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (Next.js)
  • Docs

How to test

  1. Use claude-cli to make requests to Bifrost with passthrough enabled
  2. Verify that authentication headers are properly handled
  3. Test WebSearch tool functionality with claude-cli
# Core/Transports
go version
go test ./...

Breaking changes

  • Yes
  • No

Related issues

Fixes issues with claude-cli authentication and WebSearch tool handling.

Security considerations

Ensures proper authentication header handling for Claude Code requests.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a centralized predicate IsClaudeCodeRequest(ctx) and applies it across Anthropic request construction, streaming, and transport passthrough to avoid injecting X-Api-Key for Claude CLI requests; replaces ad-hoc User-Agent checks and adjusts WebSearch/delta emission branches to use the predicate.

Changes

Cohort / File(s) Summary
Claude Code Detection Utility
core/providers/anthropic/utils.go
Add exported IsClaudeCodeRequest(*schemas.BifrostContext) bool to detect "claude-cli" User-Agent.
Anthropic provider: headers, streaming & responses
core/providers/anthropic/anthropic.go, core/providers/anthropic/responses.go
Replace inline User-Agent checks with IsClaudeCodeRequest(ctx); only set X-Api-Key when key != "" && !IsClaudeCodeRequest(ctx); gate WebSearch sanitization, synthetic input_json_delta emission, and content-block stop events on the predicate; remove an internal conversion helper.
Transport passthrough & context flags
transports/bifrost-http/integrations/anthropic.go
Use anthropic.IsClaudeCodeRequest(ctx) for passthrough decisions instead of local User-Agent parsing; set SendBackRawResponse when passthrough enabled; preserve RawResponse passthrough and add fallback to converted/combined stream payload when needed.
Tests / Selectors / E2E helpers
tests/integrations/python/config.json, tests/e2e/**/pages/*.ts, tests/e2e/**/utils/*.ts
Add "name" metadata to provider key entries; tighten toast selectors to exclude removed toasts and update tests to use dialog-based provider config flows and stable numeric input helpers.
UI: provider config modal & keys table changes
ui/app/workspace/providers/dialogs/providerConfigSheet.tsx, ui/app/workspace/providers/views/modelProviderConfig.tsx, ui/app/workspace/providers/views/modelProviderKeysTableView.tsx, ui/app/workspace/providers/fragments/performanceFormFragment.tsx
Introduce ProviderConfigSheet modal, wire Edit Provider Config action; replace accordion/tabs flow with sheet; add headerActions and isKeyless props to keys table and keyless UI; adjust form effects/triggers for performance fields.
UI: routing, sidebar & routing rules
ui/app/workspace/routing-rules/views/routingRulesView.tsx, ui/components/sidebar.tsx
Add RBAC gating and editing state to routing rules UI; make navigation handlers accept optional mouse event and respect modifier keys for external/new-tab navigation.
Validation schema changes
ui/lib/schemas/providerForm.ts, ui/lib/types/schemas.ts
Tighten concurrency/buffer validation: switch from coerced numbers to explicit number types and enforce concurrency <= buffer_size with refined error path/message.
Misc / Build & manifests
transports/bifrost-http/.air.toml, go.mod, package.json
Minor manifest/build adjustments and excludes added.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Gateway as Bifrost Gateway
    participant Transport as Bifrost Transport
    participant AnthropicAPI as Anthropic API

    Client->>Gateway: HTTP request (includes User-Agent)
    Gateway->>Gateway: Build BifrostContext (ctx)
    Gateway->>Gateway: IsClaudeCodeRequest(ctx)?
    alt Claude CLI request
        Gateway->>Transport: Forward request WITHOUT X-Api-Key (passthrough flag set)
        Transport->>AnthropicAPI: Send request (Authorization may be present)
        AnthropicAPI-->>Transport: Response / SSE stream (RawResponse possible)
        Transport->>Gateway: Return RawResponse (or fallback converted stream)
        Gateway-->>Client: Return response/stream (passthrough)
    else Non-Claude request
        Gateway->>Gateway: Inject X-Api-Key header
        Gateway->>Transport: Forward request WITH X-Api-Key
        Transport->>AnthropicAPI: Send request
        AnthropicAPI-->>Transport: Response / SSE stream
        Transport->>Gateway: Convert/process stream into Anthropic responses
        Gateway-->>Client: Return processed response/stream
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I sniff the headers, hop and peek,
Claude walks by — I skip the leak,
No key in paw, no noisy squeak,
Streams stay calm, the code is sleek,
A tidy hop, a little tweak.

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes are directly scoped to fixing the API key injection issue. However, several unrelated UI and test changes appear out of scope: ProviderConfigSheet (new component), ModelProviderConfig refactoring, RoutingRulesView changes, sidebar navigation modifications, and test helper updates for toast handling. Review whether UI component additions (ProviderConfigSheet, toast selector changes, sidebar navigation) and routing rules modifications are intentional scope creep or necessary supporting infrastructure. Consider separating out-of-scope changes into separate PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main objective: preventing API key forwarding for Claude Code requests, which is the core security fix of this PR.
Description check ✅ Passed The PR description follows the template with all major sections completed: Summary, Issues (Closes #1551), Changes, Type of change, Affected areas, How to test, Breaking changes, and Security considerations. All checklist items are marked as complete.
Linked Issues check ✅ Passed The PR successfully addresses the linked issue #1551: it prevents X-Api-Key injection for Claude Code requests by extracting detection logic into IsClaudeCodeRequest and applying conditional header injection across request paths and streaming.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 02-09-dont_use_forward_api_key_in_case_of_claude_code_request

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

Copy link
Contributor Author

akshaydeo commented Feb 9, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

🧪 Test Suite Available

This PR can be tested by a repository admin.

Run tests for PR #1585

@akshaydeo akshaydeo marked this pull request as ready for review February 9, 2026 09:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@core/providers/anthropic/responses.go`:
- Line 14: The file is importing its own package which causes an import cycle;
remove the self-import "github.com/maximhq/bifrost/core/providers/anthropic"
from core/providers/anthropic/responses.go and replace all qualified calls that
use the package prefix (e.g. anthropic.IsClaudeCodeRequest) with the unqualified
function name IsClaudeCodeRequest (the function is declared in utils.go in the
same package); update every occurrence of the qualified call sites in
responses.go to call IsClaudeCodeRequest directly.

@akshaydeo akshaydeo force-pushed the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch from e46deb8 to 2f14280 Compare February 9, 2026 09:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@core/providers/anthropic/utils.go`:
- Around line 1593-1600: The integration layer is doing a case-sensitive check
on the raw User-Agent so "Claude-CLI" never sets the context; update the check
in transports/bifrost-http/integrations/anthropic.go (the logic around the
User-Agent check that currently gates setting the BifrostContextKeyUserAgent) to
perform a case-insensitive match (e.g., normalize userAgent with
strings.ToLower() or use a case-insensitive comparison) before deciding to set
the context value; leave IsClaudeCodeRequest in
core/providers/anthropic/utils.go unchanged since it consumes the normalized
context value.

@akshaydeo akshaydeo force-pushed the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch from 2f14280 to 80728ba Compare February 9, 2026 11:45
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ui/app/workspace/providers/views/modelProviderConfig.tsx (1)

22-27: ⚠️ Potential issue | 🟡 Minor

Add custom_provider_config presence to memo dependencies.
When custom_provider_config transitions from undefined to a defined object (even without is_key_less), the memo logic changes—it switches from returning keysRequired(provider.name) to returning !(provider.custom_provider_config?.is_key_less ?? false). The current dependency on only is_key_less misses this object presence change. Include the object itself in the dependency array to capture the transition.

✏️ Suggested fix
- }, [provider.name, provider.custom_provider_config?.is_key_less]);
+ }, [provider.name, provider.custom_provider_config]);
🤖 Fix all issues with AI agents
In `@ui/app/workspace/providers/dialogs/providerConfigSheet.tsx`:
- Around line 81-83: The grid column count is hardcoded as repeat(${tabs.length
+ 3}, 1fr) causing extra empty columns; update the TabsList gridTemplateColumns
to use the actual rendered tab count (e.g., repeat(${tabs.length}, 1fr)) or
compute tabs.length plus the exact number of additional controls if there are
extra action buttons, so the number of columns matches the UI elements; locate
this change in the TabsList usage in providerConfigSheet.tsx.
🧹 Nitpick comments (2)
ui/components/sidebar.tsx (1)

849-862: Indentation inconsistency on cleanup code.

The logic is correct, but lines 859-861 have extra indentation compared to the surrounding code within the same block. This is a minor formatting nit.

🧹 Suggested formatting fix
 		} else if (e.key === "Enter") {
 			e.preventDefault();
 			const target = navigableItems[focusedIndex];
 			if (target) {
 				const url = target.queryParam ? `${target.url}?tab=${target.queryParam}` : target.url;
 				if (target.isExternal || e.metaKey || e.ctrlKey) {
 					window.open(url, "_blank");
 				} else {
 					router.push(url);
 				}
-					setSearchQuery("");
-					setFocusedIndex(-1);
-					searchInputRef.current?.blur();
+				setSearchQuery("");
+				setFocusedIndex(-1);
+				searchInputRef.current?.blur();
 			}
core/providers/anthropic/anthropic.go (1)

866-869: Fix applied correctly; minor trailing whitespace.

The logic is correct and consistent. Line 866 has trailing whitespace after the tab character which could be cleaned up for consistency.

🧹 Suggested formatting fix
-	
-	if key.Value.GetValue() != "" && !IsClaudeCodeRequest(ctx) {
+	if key.Value.GetValue() != "" && !IsClaudeCodeRequest(ctx) {

@akshaydeo akshaydeo force-pushed the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch from 80728ba to c984fd6 Compare February 9, 2026 12:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ui/app/workspace/routing-rules/views/routingRulesView.tsx (1)

21-36: ⚠️ Potential issue | 🟠 Major

Add canEdit permission check to gate the Edit action, consistent with Delete handling.

The Edit button in RoutingRulesTable (line 165) renders unconditionally despite RbacOperation.Update being defined in the RBAC model. Meanwhile, the Delete button (line 168) correctly respects the canDelete permission. Add a canEdit check in routingRulesView.tsx (checking RbacOperation.Update) and pass it to RoutingRulesTable as a prop, then gate the Edit button similar to how Delete is gated. This prevents unauthorized users from seeing and triggering the edit UI.

ui/app/workspace/providers/views/modelProviderConfig.tsx (1)

22-27: ⚠️ Potential issue | 🟠 Major

showApiKeys memo can go stale when custom config presence flips.

The dependency list only tracks provider.custom_provider_config?.is_key_less; if custom_provider_config is added/removed while is_key_less remains undefined, the memo won't recompute and the isKeyless prop passed to ModelProviderKeysTableView (line 39) will reflect stale logic. Include the presence check in deps or compute inline.

🐛 Proposed fix
-	}, [provider.name, provider.custom_provider_config?.is_key_less]);
+	}, [provider.name, !!provider.custom_provider_config, provider.custom_provider_config?.is_key_less]);
🤖 Fix all issues with AI agents
In `@ui/app/workspace/providers/views/modelProviderKeysTableView.tsx`:
- Around line 111-115: The copy shown when isKeyless is true assumes a header
action exists; update the JSX in the isKeyless rendering block (the conditional
that renders the keyless message) to either (a) check for the presence of
headerActions and render “You can edit the provider configuration using the
button above.” only when headerActions is provided, otherwise render a neutral
message like “You can edit the provider configuration from the provider
settings.”, or (b) enforce headerActions via prop validation/TypeScript types so
callers must supply it; adjust the message logic in the component where
isKeyless is used accordingly (refer to isKeyless and headerActions in
modelProviderKeysTableView.tsx).
🧹 Nitpick comments (1)
transports/bifrost-http/integrations/anthropic.go (1)

115-125: Consider logging marshal failures for observability.

When marshaling events for the combined response, errors are silently ignored with continue. While this is reasonable for streaming (to send as much data as possible), silent failures can make debugging difficult in production.

🔧 Add debug logging for marshal failures
 for _, event := range anthropicResponse {
     responseJSON, err := sonic.Marshal(event)
     if err != nil {
+        // Log at debug level to aid troubleshooting without noise
+        if logger := ctx.Value(schemas.BifrostContextKeyLogger); logger != nil {
+            if l, ok := logger.(schemas.Logger); ok {
+                l.Debug("failed to marshal stream event", "error", err, "eventType", event.Type)
+            }
+        }
         continue
     }

@akshaydeo akshaydeo force-pushed the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch from c984fd6 to 670f5b4 Compare February 9, 2026 13:57
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ui/app/workspace/providers/fragments/performanceFormFragment.tsx (1)

44-54: ⚠️ Potential issue | 🟠 Major

Guard the reset to avoid overwriting unsaved edits.
This effect resets whenever provider fields change, even if the user has unsaved edits, which can wipe input. Add an isDirty guard (or keepDirty) before resetting.

🛠️ Suggested fix
-useEffect(() => {
-  // Reset form with new provider's concurrency_and_buffer_size when provider changes
-  form.reset({
-    concurrency_and_buffer_size: {
-      concurrency: provider.concurrency_and_buffer_size?.concurrency ?? DefaultPerformanceConfig.concurrency,
-      buffer_size: provider.concurrency_and_buffer_size?.buffer_size ?? DefaultPerformanceConfig.buffer_size,
-    },
-    send_back_raw_request: provider.send_back_raw_request ?? false,
-    send_back_raw_response: provider.send_back_raw_response ?? false,
-  });
-}, [form, provider.name, provider.concurrency_and_buffer_size, provider.send_back_raw_request, provider.send_back_raw_response]);
+useEffect(() => {
+  if (form.formState.isDirty) return;
+  // Reset form with new provider's concurrency_and_buffer_size when provider changes
+  form.reset({
+    concurrency_and_buffer_size: {
+      concurrency: provider.concurrency_and_buffer_size?.concurrency ?? DefaultPerformanceConfig.concurrency,
+      buffer_size: provider.concurrency_and_buffer_size?.buffer_size ?? DefaultPerformanceConfig.buffer_size,
+    },
+    send_back_raw_request: provider.send_back_raw_request ?? false,
+    send_back_raw_response: provider.send_back_raw_response ?? false,
+  });
+}, [
+  form,
+  form.formState.isDirty,
+  provider.name,
+  provider.concurrency_and_buffer_size,
+  provider.send_back_raw_request,
+  provider.send_back_raw_response,
+]);
Based on learnings: “In the Bifrost UI, when an external state change may affect a form (e.g., provider changes), do not reset the form or trigger updates if the user has unsaved edits. Check form.formState.isDirty and preserve user edits; only update underlying information or navigate/redirect when the form is clean.”
🤖 Fix all issues with AI agents
In `@ui/components/sidebar.tsx`:
- Around line 218-234: The two window.open calls in handleNavigation and
handleSubItemClick are vulnerable to reverse tabnabbing; add the noopener and
noreferrer protections and centralize the logic by creating a helper (e.g.,
openInNewTab(url: string)) that calls window.open(url, "_blank",
"noopener,noreferrer") and use that helper from both handleNavigation and
handleSubItemClick instead of calling window.open directly. Ensure the helper is
imported/defined in the same module and used both where isExternal/ctrl/meta
clicks are handled so the behavior is consistent and maintainable.
🧹 Nitpick comments (1)
tests/e2e/features/providers/pages/providers.page.ts (1)

402-411: Align the helper comment with the actual interaction.
The comment mentions native value setter/input event dispatch, but the implementation selects and types. Suggest updating the comment to avoid drift.

♻️ Suggested comment update
-  /**
-   * Fill a React controlled number input by using the native value setter
-   * and dispatching an input event. This bypasses React's value tracker
-   * to reliably update controlled input components.
-   */
+  /**
+   * Fill a controlled number input by selecting all text and typing sequentially.
+   * Mirrors user input to ensure React-controlled fields update reliably.
+   */

@akshaydeo akshaydeo force-pushed the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch 2 times, most recently from 74bbd13 to 65e9318 Compare February 9, 2026 15:13
@akshaydeo akshaydeo force-pushed the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch from 65e9318 to 5e5d631 Compare February 9, 2026 15:28
@akshaydeo akshaydeo mentioned this pull request Feb 9, 2026
16 tasks
Copy link
Contributor Author

akshaydeo commented Feb 9, 2026

Merge activity

  • Feb 9, 5:06 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 9, 5:16 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo akshaydeo merged commit 8f24148 into main Feb 9, 2026
9 checks passed
@akshaydeo akshaydeo deleted the 02-09-dont_use_forward_api_key_in_case_of_claude_code_request branch February 9, 2026 17:16
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.

[Bug]: Anthropic provider : gateway incorrectly injects X-Api-Key even for OAuth accounts

1 participant