Skip to content

feat: support Anthropic OAuth tokens in passthrough endpoint#20429

Closed
klaudworks wants to merge 2 commits intoBerriAI:mainfrom
klaudworks:feat/anthropic-oauth-passthrough
Closed

feat: support Anthropic OAuth tokens in passthrough endpoint#20429
klaudworks wants to merge 2 commits intoBerriAI:mainfrom
klaudworks:feat/anthropic-oauth-passthrough

Conversation

@klaudworks
Copy link
Copy Markdown

Summary

Enables Anthropic OAuth token authentication (used by Claude Code / Claude Max) through the /anthropic/{endpoint:path} passthrough endpoint.

Problem

When users authenticate with Claude Max OAuth tokens (sk-ant-oat...), the passthrough endpoint was overwriting the Authorization header with the server's x-api-key, causing authentication failures.

Solution

  • Detect OAuth tokens by checking if Authorization header starts with Bearer sk-ant-oat
  • For OAuth requests: forward all headers as-is (don't set x-api-key)
  • For non-OAuth requests: use server's ANTHROPIC_API_KEY as before
  • Customer can still override headers via x-pass-* prefix in both cases

Changes

  • litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py: Add OAuth detection logic
  • tests/pass_through_unit_tests/test_anthropic_passthrough_oauth.py: Add unit tests

Testing

  • Added 8 unit tests covering OAuth detection, non-OAuth fallback, case sensitivity, and header forwarding
  • All existing passthrough tests continue to pass

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Feb 4, 2026

CLA assistant check
All committers have signed the CLA.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 4, 2026 6:30pm

Request Review

The passthrough endpoint was broken in several ways:
1. Client x-api-key was overwritten by server credentials
2. Client Authorization header (OAuth) was overwritten
3. If server had no ANTHROPIC_API_KEY configured, it sent 'None' as the key
4. x-litellm-api-key was leaked to upstream providers

Now uses correct priority:
1. Client x-api-key -> forward as-is
2. Client Authorization -> forward as-is
3. No client auth -> use server credentials (if configured)
4. Nothing -> forward without credentials (let Anthropic return auth error)

Also fixes security issue where x-litellm-api-key was forwarded to providers.
@klaudworks klaudworks marked this pull request as ready for review February 4, 2026 18:31
@klaudworks klaudworks closed this Feb 4, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 4, 2026

Greptile Overview

Greptile Summary

Fixed Anthropic passthrough endpoint to respect client-provided credentials instead of always overwriting with server credentials. Also fixed security issue where x-litellm-api-key was leaked to upstream providers.

Key Changes:

  • Modified anthropic_proxy_route to check for client x-api-key or authorization headers before using server credentials
  • Added x-litellm-api-key to the list of headers stripped before forwarding to upstream providers
  • Added unit tests for header forwarding behavior

Issues Found:

  • PR description mentions OAuth token detection (sk-ant-oat...) but implementation checks for ANY auth headers, not specifically OAuth tokens
  • Missing integration tests for the credential priority logic
  • Code structure could be simplified

Confidence Score: 3/5

  • PR is mostly safe to merge but has a discrepancy between description and implementation
  • Core fix is sound (respecting client credentials and preventing credential leakage), but PR description claims OAuth-specific detection that doesn't exist in the code. Implementation applies to all client credentials, not just OAuth tokens. Missing integration tests for the credential priority logic.
  • Pay close attention to litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py - verify the behavior matches your intent

Important Files Changed

Filename Overview
litellm/passthrough/utils.py Added x-litellm-api-key to list of headers that should not be forwarded to upstream providers (security fix)
litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Modified anthropic_proxy_route to respect client-provided credentials instead of always overwriting with server credentials. Logic doesn't specifically detect OAuth tokens as PR description claims.
tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_endpoints_common_utils.py Added unit tests for header forwarding behavior, verifying x-litellm-api-key is stripped and other headers are preserved

Sequence Diagram

sequenceDiagram
    participant Client
    participant Proxy as LiteLLM Proxy
    participant Route as anthropic_proxy_route
    participant PassThrough as pass_through_request
    participant Utils as HttpPassThroughEndpointHelpers
    participant Anthropic as Anthropic API

    Client->>Proxy: POST /anthropic/v1/messages
    Note over Client,Proxy: With auth headers or without

    Proxy->>Route: Handle request
    
    alt Client provides auth header
        Route->>Route: Set custom_headers = empty dict
        Note over Route: Forward client credentials as-is
    else No client credentials
        Route->>Route: Get server credentials
        alt Server has credentials
            Route->>Route: Set custom_headers with server credentials
        else No server credentials
            Route->>Route: Set custom_headers = empty dict
        end
    end

    Route->>PassThrough: create_pass_through_route(custom_headers, _forward_headers=True)
    
    PassThrough->>Utils: forward_headers_from_request()
    Utils->>Utils: Strip: content-length, host, x-litellm-api-key
    Utils->>Utils: Merge request_headers with custom_headers
    Utils->>Utils: Process x-pass-* prefixed headers
    Utils-->>PassThrough: Final headers
    
    PassThrough->>Anthropic: Forward request with final headers
    Anthropic-->>PassThrough: Response
    PassThrough-->>Client: Return response
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +596 to +609
x_api_key_header = request.headers.get("x-api-key", "")
auth_header = request.headers.get("authorization", "")

if x_api_key_header or auth_header:
custom_headers = {}
else:
anthropic_api_key = passthrough_endpoint_router.get_credentials(
custom_llm_provider="anthropic",
region_name=None,
)
if anthropic_api_key:
custom_headers = {"x-api-key": anthropic_api_key}
else:
custom_headers = {}
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.

PR description claims this detects OAuth tokens (pattern: Bearer sk-ant-oat...), but the code doesn't check for OAuth tokens specifically. It checks if ANY x-api-key or authorization header exists, which means all client credentials bypass server credentials. If you specifically want OAuth detection as described, consider checking for the OAuth token pattern.

Prompt To Fix With AI
This is a comment left during a code review.
Path: litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
Line: 596:609

Comment:
PR description claims this detects OAuth tokens (pattern: `Bearer sk-ant-oat...`), but the code doesn't check for OAuth tokens specifically. It checks if ANY `x-api-key` or `authorization` header exists, which means all client credentials bypass server credentials. If you specifically want OAuth detection as described, consider checking for the OAuth token pattern.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +599 to +609
if x_api_key_header or auth_header:
custom_headers = {}
else:
anthropic_api_key = passthrough_endpoint_router.get_credentials(
custom_llm_provider="anthropic",
region_name=None,
)
if anthropic_api_key:
custom_headers = {"x-api-key": anthropic_api_key}
else:
custom_headers = {}
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.

Nested if-else creates empty dict in multiple paths. Consider simplifying with ternary operator for the inner condition.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
Line: 599:609

Comment:
Nested if-else creates empty dict in multiple paths. Consider simplifying with ternary operator for the inner condition.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 4, 2026

Additional Comments (1)

tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_endpoints_common_utils.py
Tests only verify header stripping logic but don't test the new OAuth/credential priority logic in anthropic_proxy_route. Add integration tests that verify:

  • Client headers are forwarded instead of server credentials
  • Server credentials are used when client provides neither
Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/test_litellm/proxy/pass_through_endpoints/test_passthrough_endpoints_common_utils.py
Line: 98:142

Comment:
Tests only verify header stripping logic but don't test the new OAuth/credential priority logic in `anthropic_proxy_route`. Add integration tests that verify:
- Client headers are forwarded instead of server credentials
- Server credentials are used when client provides neither

How can I resolve this? If you propose a fix, please make it concise.

pkieszcz added a commit to Bitropy/litellm that referenced this pull request Mar 19, 2026
Client-provided credentials now take precedence over server credentials
in the /anthropic/ passthrough endpoint. This enables mixed mode where:

1. Client sends x-api-key → forwarded as-is (user pays via own API key)
2. Client sends Authorization → forwarded as-is (user pays via OAuth/Max)
3. No client credentials + server ANTHROPIC_API_KEY → server key used
4. No client credentials + no server key → no credentials forwarded

Previously the server always sent x-api-key (even literal "None" when
unconfigured), overwriting any client-provided credentials and breaking
Claude Code Max (OAuth) and BYOK scenarios.

Supersedes the simpler one-liner from d742c76 on v1.81.12-stable-patched.
Based on the approach from PR BerriAI#20429 (closed) and reverted PR BerriAI#14821.
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