Skip to content

Comments

fix: use Bearer auth for ANTHROPIC_AUTH_TOKEN and add ANTHROPIC_MODEL#197

Open
worldofgeese wants to merge 1 commit intospacedriveapp:mainfrom
worldofgeese:pr-135
Open

fix: use Bearer auth for ANTHROPIC_AUTH_TOKEN and add ANTHROPIC_MODEL#197
worldofgeese wants to merge 1 commit intospacedriveapp:mainfrom
worldofgeese:pr-135

Conversation

@worldofgeese
Copy link

@worldofgeese worldofgeese commented Feb 24, 2026

Summary

This PR adds the missing piece from #135: proper handling of ANTHROPIC_AUTH_TOKEN to use Bearer authentication instead of x-api-key header. This fixes 403 errors when using corporate Anthropic-compatible proxies.

Also adds ANTHROPIC_MODEL env var as requested by @pjv.

Changes

Auth Header Fix

When ANTHROPIC_AUTH_TOKEN is used (detected by checking if the key came from that env var vs ANTHROPIC_API_KEY), the request now uses:

  • Authorization: Bearer <token> header
  • No Claude Code identity headers (user-agent, x-app)
  • Fine-grained streaming beta flag only (no OAuth/Claude Code betas)

This matches how Claude Code itself handles the distinction between API keys and auth tokens.

New Auth Path Variant

Added AnthropicAuthPath::AuthToken alongside existing ApiKey and OAuthToken:

  • ApiKeyx-api-key header (native Anthropic)
  • OAuthTokenAuthorization: Bearer + identity headers (Claude Code OAuth)
  • AuthToken (new) → Authorization: Bearer only (proxy tokens)

ANTHROPIC_MODEL Env Var

Sets all anthropic/* routes at once. Example:

ANTHROPIC_MODEL=claude-opus-4-6

This sets channel, branch, worker, compactor, and cortex all to anthropic/claude-opus-4-6.

Files Changed

  • src/llm/anthropic/auth.rs - New AuthToken variant, updated detect_auth_path and apply_auth_headers
  • src/config.rs - is_auth_token tracking in ProviderConfig, ANTHROPIC_MODEL support
  • src/llm/anthropic/params.rs - Thread is_auth_token through request building
  • src/llm/model.rs - Pass is_auth_token from provider config
  • src/api/providers.rs, src/llm/manager.rs - Add is_auth_token: false to test providers

Testing

Added 3 new unit tests for AuthToken:

  • auth_token_uses_bearer_header
  • auth_token_has_no_identity_headers
  • auth_token_has_streaming_beta_but_no_oauth_beta

Notes

  • CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC was requested but deferred as it requires more infrastructure work
  • No breaking changes - existing ANTHROPIC_API_KEY users are unaffected

Fixes

Note

This change adds support for auth tokens via the ANTHROPIC_AUTH_TOKEN environment variable with proper Bearer authentication, and introduces the ANTHROPIC_MODEL environment variable to configure all Anthropic-based LLM process models in one place. The implementation detects auth token usage and automatically handles the appropriate authentication headers (Bearer without Claude Code identity), while maintaining backward compatibility with existing API key configuration.

Written by Tembo for commit 5b9074e. This will update automatically on new commits.

- Add new AnthropicAuthPath::AuthToken variant for proxy tokens
- Track is_auth_token in ProviderConfig when key comes from ANTHROPIC_AUTH_TOKEN
- Pass is_auth_token through build_anthropic_request to apply_auth_headers
- AuthToken uses Authorization: Bearer without Claude Code identity headers
- Add ANTHROPIC_MODEL env var to set all anthropic/* routes
- Update tests for new AuthToken variant

Fixes 403 errors when using ANTHROPIC_AUTH_TOKEN with corporate proxies.
Closes discussion in spacedriveapp#135 about auth header mismatch.

Note: CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC is deferred for future work.
@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Walkthrough

This PR adds support for Anthropic Bearer token authentication via ANTHROPIC_AUTH_TOKEN by introducing an is_auth_token: bool field to ProviderConfig. The flag is propagated through configuration loading, authentication detection, and request building to distinguish Bearer tokens from API keys during header construction.

Changes

Cohort / File(s) Summary
Configuration and Provider Setup
src/config.rs, src/api/providers.rs, src/llm/manager.rs
Added is_auth_token field initialization to ProviderConfig struct. Environment loading sets the flag based on ANTHROPIC_AUTH_TOKEN presence; TOML and test paths default to false. Updated all provider construction sites.
Anthropic Authentication Path
src/llm/anthropic/auth.rs
Added AuthToken enum variant to AnthropicAuthPath. Updated detect_auth_path and apply_auth_headers signatures to accept is_auth_token flag, routing to new Bearer auth path when true. Added corresponding test coverage.
Request Building
src/llm/anthropic/params.rs
Added is_auth_token parameter to build_anthropic_request. Propagated flag to detect_auth_path and apply_auth_headers calls for proper authentication routing.
Model Layer Integration
src/llm/model.rs
Updated call_anthropic to pass provider_config.is_auth_token to request builder. Broadened OAuth condition to include AuthToken variant.

Sequence Diagram

sequenceDiagram
    participant Config as Configuration<br/>(load_from_env)
    participant AuthDetect as Auth Detection<br/>(detect_auth_path)
    participant AuthApply as Auth Headers<br/>(apply_auth_headers)
    participant Request as Request Builder<br/>(build_anthropic_request)

    Config->>Config: Determine is_auth_token<br/>(ANTHROPIC_AUTH_TOKEN present?)
    Config->>Request: Pass is_auth_token flag
    Request->>AuthDetect: Call with token & is_auth_token
    AuthDetect->>AuthDetect: Branch: is_auth_token=true?
    alt Bearer Token (AuthToken path)
        AuthDetect->>AuthApply: Return AuthToken variant
        AuthApply->>AuthApply: Apply Bearer Authorization<br/>header only
    else API Key/OAuth (existing paths)
        AuthDetect->>AuthApply: Return ApiKey/OAuthToken
        AuthApply->>AuthApply: Apply existing auth headers
    end
    AuthApply->>Request: Return configured RequestBuilder
    Request->>Request: Build complete request
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A token bearer hops along the auth-path track,
With is_auth_token flags keeping Bearer-auth in check,
No more mixing keys with tokens, what a sight,
Configuration routes them straight, left or right,
Headers flow like carrots through the request delight! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: Bearer auth support for ANTHROPIC_AUTH_TOKEN and the new ANTHROPIC_MODEL env var.
Description check ✅ Passed The description comprehensively explains the changes, including auth header fixes, new auth path variant, ANTHROPIC_MODEL support, files changed, testing, and backward compatibility notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@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)
src/llm/anthropic/auth.rs (1)

96-107: ⚠️ Potential issue | 🟠 Major

Update detect_auth_path test calls for new signature.

The unit tests still call detect_auth_path with one argument, so the test module won’t compile.

🧪 Update test calls
-        detect_auth_path("sk-ant-oat01-abc123"),
+        detect_auth_path("sk-ant-oat01-abc123", false),
         AnthropicAuthPath::OAuthToken
     );
 }

@@
-        detect_auth_path("sk-ant-api03-xyz789"),
+        detect_auth_path("sk-ant-api03-xyz789", false),
         AnthropicAuthPath::ApiKey
     );
 }

@@
-        detect_auth_path("some-random-key"),
+        detect_auth_path("some-random-key", false),
         AnthropicAuthPath::ApiKey
     );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/llm/anthropic/auth.rs` around lines 96 - 107, Tests failing because
detect_auth_path now requires a second boolean parameter is_auth_token; update
all test calls to pass the appropriate is_auth_token value. Find usages in the
test module that call detect_auth_path(token) and change them to
detect_auth_path(token, true) or detect_auth_path(token, false) depending on
whether the test intends the token to come from ANTHROPIC_AUTH_TOKEN; ensure
test cases asserting AnthropicAuthPath::AuthToken use true and other cases use
false.
src/llm/anthropic/params.rs (1)

54-107: ⚠️ Potential issue | 🟠 Major

AuthToken shouldn’t trigger Claude Code preamble/tool normalization.

is_oauth now treats AuthToken like OAuth, which injects Claude Code identity behavior and tool renaming. That contradicts the “bearer-only, no identity” intent for ANTHROPIC_AUTH_TOKEN. Keep OAuth behavior only for OAuthToken (and mirror in model.rs).

🔧 Suggested change
-    let is_oauth = auth_path == AnthropicAuthPath::OAuthToken || auth_path == AnthropicAuthPath::AuthToken;
+    let is_oauth = auth_path == AnthropicAuthPath::OAuthToken;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/llm/anthropic/params.rs` around lines 54 - 107, The code sets is_oauth by
comparing auth::detect_auth_path(...) to both AnthropicAuthPath::OAuthToken and
AnthropicAuthPath::AuthToken, which causes AuthToken to receive OAuth-only
behavior; change the is_oauth check in build_anthropic_request to be true only
when auth::detect_auth_path(...) == AnthropicAuthPath::OAuthToken so
ANTHROPIC_AUTH_TOKEN remains "bearer-only, no identity", and adjust any related
logic that branches on is_oauth (e.g., build_system_prompt and build_tools
calls) accordingly; also mirror this fix in the corresponding logic in model.rs
where detect_auth_path is evaluated.
♻️ Duplicate comments (1)
src/llm/model.rs (1)

362-363: Align AuthToken handling with non‑OAuth tool remap.

Same concern as noted in src/llm/anthropic/params.rs: if AuthToken is meant to be bearer-only, it shouldn’t trigger Claude Code tool remapping here either.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/llm/model.rs` around lines 362 - 363, The check that sets is_oauth
currently treats AnthropicAuthPath::AuthToken the same as OAuthToken; change the
logic so only AnthropicAuthPath::OAuthToken makes is_oauth true (i.e., remove
the AuthToken branch) so bearer-only AuthToken does not trigger Claude Code tool
remapping; update the condition using anthropic_request.auth_path and the
AnthropicAuthPath::OAuthToken enum variant accordingly (leave any other uses of
AnthropicAuthPath::AuthToken untouched).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config.rs`:
- Around line 2078-2086: The current boolean anthropic_is_auth_token only
becomes true when no TOML key is present, so a TOML anthropic_key that
references env:ANTHROPIC_AUTH_TOKEN still selects x-api-key; update the logic to
also detect when toml.llm.anthropic_key explicitly references the environment
variable ANTHROPIC_AUTH_TOKEN (e.g., the raw string contains or parses as
"env:ANTHROPIC_AUTH_TOKEN" or resolve_env_value indicates an env-ref) and treat
that case as using auth token; modify the condition around
anthropic_is_auth_token (and the duplicate logic at the later block around the
2218-2229 occurrence) to check both the absence of a resolved TOML key and the
presence of an env reference to ANTHROPIC_AUTH_TOKEN before consulting
std::env::var.

---

Outside diff comments:
In `@src/llm/anthropic/auth.rs`:
- Around line 96-107: Tests failing because detect_auth_path now requires a
second boolean parameter is_auth_token; update all test calls to pass the
appropriate is_auth_token value. Find usages in the test module that call
detect_auth_path(token) and change them to detect_auth_path(token, true) or
detect_auth_path(token, false) depending on whether the test intends the token
to come from ANTHROPIC_AUTH_TOKEN; ensure test cases asserting
AnthropicAuthPath::AuthToken use true and other cases use false.

In `@src/llm/anthropic/params.rs`:
- Around line 54-107: The code sets is_oauth by comparing
auth::detect_auth_path(...) to both AnthropicAuthPath::OAuthToken and
AnthropicAuthPath::AuthToken, which causes AuthToken to receive OAuth-only
behavior; change the is_oauth check in build_anthropic_request to be true only
when auth::detect_auth_path(...) == AnthropicAuthPath::OAuthToken so
ANTHROPIC_AUTH_TOKEN remains "bearer-only, no identity", and adjust any related
logic that branches on is_oauth (e.g., build_system_prompt and build_tools
calls) accordingly; also mirror this fix in the corresponding logic in model.rs
where detect_auth_path is evaluated.

---

Duplicate comments:
In `@src/llm/model.rs`:
- Around line 362-363: The check that sets is_oauth currently treats
AnthropicAuthPath::AuthToken the same as OAuthToken; change the logic so only
AnthropicAuthPath::OAuthToken makes is_oauth true (i.e., remove the AuthToken
branch) so bearer-only AuthToken does not trigger Claude Code tool remapping;
update the condition using anthropic_request.auth_path and the
AnthropicAuthPath::OAuthToken enum variant accordingly (leave any other uses of
AnthropicAuthPath::AuthToken untouched).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7170ca0 and 5b9074e.

📒 Files selected for processing (6)
  • src/api/providers.rs
  • src/config.rs
  • src/llm/anthropic/auth.rs
  • src/llm/anthropic/params.rs
  • src/llm/manager.rs
  • src/llm/model.rs

Comment on lines +2078 to +2086
// Track whether ANTHROPIC_AUTH_TOKEN is being used (for Bearer auth)
let anthropic_is_auth_token = toml
.llm
.anthropic_key
.as_deref()
.and_then(resolve_env_value)
.is_none()
&& std::env::var("ANTHROPIC_API_KEY").is_err()
&& std::env::var("ANTHROPIC_AUTH_TOKEN").is_ok();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Config env:ANTHROPIC_AUTH_TOKEN won’t set is_auth_token.

anthropic_is_auth_token only flips when no TOML key is present, so a config that references env:ANTHROPIC_AUTH_TOKEN will still use x‑api‑key headers. Consider detecting that env reference explicitly (or adding a config flag).

🔧 Suggested change
-        let anthropic_is_auth_token = toml
-            .llm
-            .anthropic_key
-            .as_deref()
-            .and_then(resolve_env_value)
-            .is_none()
-            && std::env::var("ANTHROPIC_API_KEY").is_err()
-            && std::env::var("ANTHROPIC_AUTH_TOKEN").is_ok();
+        let anthropic_key_raw = toml.llm.anthropic_key.as_deref();
+        let anthropic_is_auth_token =
+            matches!(anthropic_key_raw, Some("env:ANTHROPIC_AUTH_TOKEN"))
+                || (anthropic_key_raw.is_none()
+                    && std::env::var("ANTHROPIC_API_KEY").is_err()
+                    && std::env::var("ANTHROPIC_AUTH_TOKEN").is_ok());

Also applies to: 2218-2229

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.rs` around lines 2078 - 2086, The current boolean
anthropic_is_auth_token only becomes true when no TOML key is present, so a TOML
anthropic_key that references env:ANTHROPIC_AUTH_TOKEN still selects x-api-key;
update the logic to also detect when toml.llm.anthropic_key explicitly
references the environment variable ANTHROPIC_AUTH_TOKEN (e.g., the raw string
contains or parses as "env:ANTHROPIC_AUTH_TOKEN" or resolve_env_value indicates
an env-ref) and treat that case as using auth token; modify the condition around
anthropic_is_auth_token (and the duplicate logic at the later block around the
2218-2229 occurrence) to check both the absence of a resolved TOML key and the
presence of an env reference to ANTHROPIC_AUTH_TOKEN before consulting
std::env::var.

) -> AnthropicRequest {
let is_oauth = auth::detect_auth_path(api_key) == AnthropicAuthPath::OAuthToken;
let auth_path = auth::detect_auth_path(api_key, is_auth_token);
let is_oauth = auth_path == AnthropicAuthPath::OAuthToken || auth_path == AnthropicAuthPath::AuthToken;
Copy link

Choose a reason for hiding this comment

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

If AuthToken is meant to be “Bearer auth without Claude Code identity”, grouping it under is_oauth will also inject the Claude Code system preamble + tool-name normalization. I think is_oauth should stay strictly for the OAuth path.

Suggested change
let is_oauth = auth_path == AnthropicAuthPath::OAuthToken || auth_path == AnthropicAuthPath::AuthToken;
let is_oauth = auth_path == AnthropicAuthPath::OAuthToken;

Comment on lines +362 to +363
let is_oauth = anthropic_request.auth_path == crate::llm::anthropic::AnthropicAuthPath::OAuthToken
|| anthropic_request.auth_path == crate::llm::anthropic::AnthropicAuthPath::AuthToken;
Copy link

Choose a reason for hiding this comment

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

AuthToken seems like it should behave like the plain API key path (no Claude Code identity/tool renames). Including it in is_oauth makes us reverse-map tool names even though we probably didn’t normalize them.

Suggested change
let is_oauth = anthropic_request.auth_path == crate::llm::anthropic::AnthropicAuthPath::OAuthToken
|| anthropic_request.auth_path == crate::llm::anthropic::AnthropicAuthPath::AuthToken;
let is_oauth =
anthropic_request.auth_path == crate::llm::anthropic::AnthropicAuthPath::OAuthToken;

pub api_key: String,
pub name: Option<String>,
/// Whether the token came from ANTHROPIC_AUTH_TOKEN (uses Bearer auth)
pub is_auth_token: bool,
Copy link

Choose a reason for hiding this comment

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

Nice to have the provenance tracked explicitly. One thing to watch when this lands on main: adding a new required field means every ProviderConfig { ... } literal needs to set it (most should be is_auth_token: false). Might be worth a quick rg 'ProviderConfig \{' sweep on main to avoid a surprise compile break during merge resolution.

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.

1 participant