fix(proxy): derive Claude auth strategy from ANTHROPIC env var name

Anthropic SDK assigns distinct semantics to the two env vars:

- ANTHROPIC_API_KEY    -> x-api-key
- ANTHROPIC_AUTH_TOKEN -> Authorization: Bearer

The Claude adapter previously collapsed both into AuthStrategy::Anthropic
and then emitted Authorization: Bearer regardless, breaking strict
Anthropic-protocol endpoints (Anthropic official, Cloudflare AI Gateway,
OpenCode Go, DashScope) and silently overriding the user's intended auth
scheme.

- claude::extract_auth: infer strategy from env var name
  (ANTHROPIC_AUTH_TOKEN -> ClaudeAuth, ANTHROPIC_API_KEY -> Anthropic),
  matching the precedence already used by extract_key.
- claude::get_auth_headers: split the Anthropic arm so it emits
  x-api-key, while ClaudeAuth and Bearer continue to use Bearer.
- stream_check: reuse ClaudeAdapter::get_auth_headers as the single
  source of truth, replacing the prior "always Bearer + maybe x-api-key"
  double injection that produced auth conflicts and false-negative
  health checks.
- Cover each strategy -> header mapping and env-var precedence with
  new unit tests in claude.rs.

Refs #2368, #2380
This commit is contained in:
Jason
2026-05-01 19:09:46 +08:00
parent 35bce24633
commit bdc4c1e8b8
2 changed files with 125 additions and 11 deletions
+10 -7
View File
@@ -16,7 +16,9 @@ use crate::proxy::providers::copilot_auth;
use crate::proxy::providers::transform::anthropic_to_openai;
use crate::proxy::providers::transform_gemini::anthropic_to_gemini;
use crate::proxy::providers::transform_responses::anthropic_to_responses;
use crate::proxy::providers::{get_adapter, AuthInfo, AuthStrategy};
use crate::proxy::providers::{
get_adapter, AuthInfo, AuthStrategy, ClaudeAdapter, ProviderAdapter,
};
/// 健康状态枚举
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -433,12 +435,13 @@ impl StreamCheckService {
let os_name = Self::get_os_name();
let arch_name = Self::get_arch_name();
request_builder =
request_builder.header("authorization", format!("Bearer {}", auth.api_key));
// Only Anthropic official strategy adds x-api-key
if auth.strategy == AuthStrategy::Anthropic {
request_builder = request_builder.header("x-api-key", &auth.api_key);
// 鉴权头复用 ClaudeAdapter::get_auth_headers,与代理路径(forwarder)保持单一真理来源。
// - AuthStrategy::Anthropic → x-api-key
// - AuthStrategy::ClaudeAuth → Authorization: Bearer
// - AuthStrategy::Bearer → Authorization: Bearer
// 避免之前"无条件 Bearer + 条件 x-api-key 双发"导致的假阴性 / auth conflict。
for (name, value) in ClaudeAdapter::new().get_auth_headers(auth) {
request_builder = request_builder.header(name, value);
}
request_builder = request_builder