Skip to content
源码分析手册

认证优先级:分层裁决下的凭证决策逻辑

Claude Code 的认证机制并非简单的“先来后到”,也不是单一的账号源。它是一套高度结构化的解析链,决定了在当前的会话请求中,到底该把哪一套身份凭证(Credentials)注入到 API 客户端。

从定义开始

认证优先级本质上是 针对多种凭证来源的互斥裁决(Exclusive Precedence)

在 Claude Code 中,同时存在三条并行的认证路径:

  1. 第三方提供商(3P Providers):如 AWS Bedrock、Google Vertex AI、Foundry。
  2. 第一方直连(Direct Connect):包括 ANTHROPIC_API_KEY、环境变量、以及用户自定义的 apiKeyHelper 脚本。
  3. Claude.ai 托管(Managed OAuth):即通过 /login 获取的订阅态 OAuth Token。

它不是什么:它不是一个简单的“账号列表”。运行时会根据当前的环境上下文(如是否在 CI 中、是否开启了 --bare)来动态切断某些路径。

源码级拆解

这套裁决逻辑的核心入口在 claude-code-opensource/src/utils/auth.ts,而非 /login 命令。它遵循以下四个阶梯式的判断流:

1. 路径分流:OAuth 是否启用

isAnthropicAuthEnabled() 会首先执行逻辑排除:

  • Provider 优先级:如果检测到 CLAUDE_CODE_USE_BEDROCK 等第三方标志,第一方 OAuth 路径将立即退出竞争。
  • 环境硬约束:使用 --bare 启动时,OAuth 会被强制禁用,系统仅允许通过环境变量或 apiKeyHelper 提供凭证。

2. Bearer Token 来源排序

getAuthTokenSource() 负责查找有效的 Authorization 头:

  • 优先环境变量ANTHROPIC_AUTH_TOKEN 拥有极高的优先级,通常用于特定的安全网关场景。
  • 降级到托管配置:如果环境变量缺失,系统才会去查找 /login 留下的 CLAUDE_CODE_OAUTH_TOKEN
  • 特别注意:在 Trust Dialog(信任对话框)确认之前,系统会由于安全原因拒绝执行 apiKeyHelper 脚本,以防止未经授权的本地执行风险。

3. API Key 的精细化解析

getAnthropicApiKeyWithSource() 处理的是 X-Api-Key 路径:

  • 批准记忆(Approval Persistence):在交互模式下,即使环境变量中存在 ANTHROPIC_API_KEY,也必须经过用户明确的“批准”对话框,结果会被记录在 customApiKeyResponses.approved 中。
  • Helper 缓存语义apiKeyHelper 被视为显式的“主来源”。一旦配置,系统会优先尝试从 Helper 获取 key。Helper 具备 Stale-while-revalidate 语义,默认有 5 分钟的 TTL 缓存(受 CLAUDE_CODE_API_KEY_HELPER_TTL_MS 控制)。

4. 托管 OAuth 上下文(Managed Context)

这是一个极高的特殊层级。对于 Claude Desktop远程桥接(Remote Bridge) 会话,系统会识别为 isManagedOAuthContext()。在这种场景下,为了确保企业级管控的一致性,本地的环境变量或个人 API Key 不会被允许“反客为主”地篡改托管会话的认证方式。

踩坑指南

  • /login 的局限性:如果你发现 /login 后依然无法使用某些高级功能,请检查你的环境变量。如果 ANTHROPIC_API_KEY 存在且已生效,它会“压住”/login 的托管权限。
  • Provider 的独立性:一旦进入 AWS Bedrock 或 Google Vertex 路径,所有的 OAuth 刷新、Claude.ai 订阅状态判断都将不再参与,因为系统已经切换到了完全不同的 SDK 底座。
  • 信任边界(Trust Boundary):存储在项目级(Local Settings)的 apiKeyHelper 受 Workspace Trust 限制。这意味着在你通过 /add-dir 或信任该目录前,该 Helper 不会被执行。
  • 缓存的一致性apiKeyHelper 具有缓存。如果你更新了外部密钥管理器,可能需要等待缓存过期或通过 claude auth logout 强制刷新。

延伸阅读

源码锚点

📄 src/utils/auth.ts — 核心:包含 `isAnthropicAuthEnabled`、`getAuthTokenSource`、`getAnthropicApiKeyWithSource` 等所有裁决函数的聚合点。L100-129 of 2003
typescript
export function isAnthropicAuthEnabled(): boolean {
  // --bare: API-key-only, never OAuth.
  if (isBareMode()) return false

  // `claude ssh` remote: ANTHROPIC_UNIX_SOCKET tunnels API calls through a
  // local auth-injecting proxy. The launcher sets CLAUDE_CODE_OAUTH_TOKEN as a
  // placeholder iff the local side is a subscriber (so the remote includes the
  // oauth-2025 beta header to match what the proxy will inject). The remote's
  // ~/.claude settings (apiKeyHelper, settings.env.ANTHROPIC_API_KEY) MUST NOT
  // flip this — they'd cause a header mismatch with the proxy and a bogus
  // "invalid x-api-key" from the API. See src/ssh/sshAuthProxy.ts.
  if (process.env.ANTHROPIC_UNIX_SOCKET) {
    return !!process.env.CLAUDE_CODE_OAUTH_TOKEN
  }

  const is3P =
    isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) ||
    isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) ||
    isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)

  // Check if user has configured an external API key source
  // This allows externally-provided API keys to work (without requiring proxy configuration)
  const settings = getSettings_DEPRECATED() || {}
  const apiKeyHelper = settings.apiKeyHelper
  const hasExternalAuthToken =
    process.env.ANTHROPIC_AUTH_TOKEN ||
    apiKeyHelper ||
    process.env.CLAUDE_CODE_API_KEY_FILE_DESCRIPTOR

  // Check if API key is from an external source (not managed by /login)
📄 src/services/api/client.ts — 实现:请求客户端根据 Provider 分流的具体逻辑。L16-19 of 390
typescript
  getAPIProvider,
  isFirstPartyAnthropicBaseUrl,
} from 'src/utils/model/providers.js'
import { getProxyFetchOptions } from 'src/utils/proxy.js'
📄 src/hooks/useApiKeyVerification.ts — 交互:UI 线程如何预热 `apiKeyHelper` 缓存并执行验证。L29-33 of 85
typescript
    // Use skipRetrievingKeyFromApiKeyHelper to avoid executing apiKeyHelper
    // before trust dialog is shown (security: prevents RCE via settings.json)
    const { key, source } = getAnthropicApiKeyWithSource({
      skipRetrievingKeyFromApiKeyHelper: true,
    })
📄 src/cli/handlers/auth.ts — 显示:`claude auth status` 如何翻译底层认证流状态。L8-11 of 331
typescript
  type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  logEvent,
} from '../../services/analytics/index.js'
import { getSSLErrorHint } from '../../services/api/errorUtils.js'

基于 Claude Code v2.1.88 开源快照的深度分析