Book 第三十五章:认证运行时真正隔离的是什么
第六部分:迁移与附录

第三十五章:认证运行时真正隔离的是什么

拆 auth source hierarchy、managed context、OAuth 刷新和 session ingress 这几条认证链。

1. 为什么这章必须单独拆

Claude Code 的认证绝对不是“登录一下,然后拿个 token”这么简单。

从源码看,这一层至少同时在处理:

  • first-party OAuth
  • 环境变量 API key / auth token
  • apiKeyHelper
  • Bedrock / Vertex / Foundry 这类第三方 provider
  • remote / desktop / managed session 的认证隔离
  • org 级登录强制校验
  • session ingress 的独立访问凭证

这说明 Claude Code 里的 auth,不是一个页面流程,而是一套运行时仲裁系统。

相关源码锚点:

  • src/utils/auth.ts
  • src/utils/sessionIngressAuth.ts
  • src/services/oauth/index.ts
  • src/services/oauth/client.ts
  • src/commands/login/login.tsx
  • src/commands/logout/logout.tsx
  • src/services/api/sessionIngress.ts

2. 先说结论

我对这一块的判断是:

Claude Code 的认证层主要在做三件事:认证来源仲裁、宿主边界隔离、组织策略校验。

最关键的几个结论是:

  1. first-party OAuth 和第三方 provider 不是一套凭证链。
  2. managed context 会故意阻止回退到用户本地 API key。
  3. /login/logout 本质上是 runtime rebind,不只是改一个 token。
  4. session ingress 有自己独立的 access token / cookie 协议。

这说明作者真正在防的是:

  • 本地 CLI 凭证污染受管会话
  • 第三方 provider 模式下误走 first-party OAuth
  • 组织策略和实际登录身份不一致

3. 总体结构图

flowchart TD
    A["env / fd / secure store / apiKeyHelper"] --> B["auth.ts<br/>认证来源仲裁"]
    C["OAuth PKCE flow"] --> B
    D["managed context / policy / org 校验"] --> B

    B --> E["Anthropic API client"]
    B --> F["login / logout runtime"]
    B --> G["session ingress auth"]
    B --> H["remote managed settings / profile / subscription"]

4. 认证来源不是一个值,而是一条优先级链

源码锚点:

  • src/utils/auth.ts

4.1 Auth token 的来源优先级

getAuthTokenSource() 暴露得很清楚,Claude Code 会按顺序找:

  • bare mode 下只允许非 OAuth 路线
  • 非 managed 场景可读 ANTHROPIC_AUTH_TOKEN
  • CLAUDE_CODE_OAUTH_TOKEN
  • 文件描述符或 CCR 落盘 token
  • apiKeyHelper
  • 本地保存的 Claude.ai OAuth token

这说明认证来源本身就是协议。

不是“有就用”,而是“哪个上下文允许哪个来源出现”。

4.2 API key 也有独立仲裁链

getAnthropicApiKeyWithSource() 又是一套单独优先级:

  • bare mode 时优先 env key / flag helper
  • 正常模式会看 env key
  • 然后看 fd 注入
  • 再看 apiKeyHelper
  • 再看托管 keychain / config

而且这里还夹着审批和信任门槛,比如:

  • 项目级来源是否可信
  • CI / 非交互环境怎么处理

5. 认证隔离:为什么 managed session 不许随便回退

源码锚点:

  • src/utils/auth.ts

5.1 isManagedOAuthContext() 的核心语义

这一段特别值钱。

它明确在防一件事:

如果当前会话是受管的远端或桌面上下文,就不要偷偷回退到用户本地 CLI 的凭证。

这背后的设计含义很明确:

  • 会话归属边界要稳定
  • 宿主注入的身份优先于用户本地历史凭证
  • 认证来源要和会话宿主一致

5.2 isAnthropicAuthEnabled() 不是简单开关

这个函数同时会判断:

  • bare mode
  • ssh remote 特例
  • Bedrock / Vertex / Foundry 等 3P provider
  • 外部 API key / auth token
  • managed OAuth context

它表达的其实是:

当前运行模式到底还应不应该出现 first-party 登录。

6. OAuth 不是“拿到 token 就完了”

源码锚点:

  • src/services/oauth/index.ts
  • src/services/oauth/client.ts

OAuth 流程里至少有这些关键动作:

  • PKCE
  • 本地 callback listener
  • 手动 fallback
  • token 交换
  • profile 拉取
  • 按条件刷新 token

而真正值钱的是后面的运行时治理:

  • secure storage 持久化
  • 跨进程 mtime 失效
  • 401 去重处理
  • refresh lockfile 去重

也就是说,Claude Code 不是把 OAuth 当成 UI 登录流程,而是当成一个跨进程共享、可刷新、可失效的凭证基础设施。

7. /login/logout 真正做了什么

源码锚点:

  • src/commands/login/login.tsx
  • src/commands/logout/logout.tsx

7.1 /login 的本质是重新绑整条会话

登录成功后,源码里会继续做一串很“runtime”的事:

  • context.onChangeAPIKey
  • 清理签名块
  • 重置 cost state
  • 刷 remote managed settings
  • 刷 policy limits
  • 清用户缓存
  • 刷 GrowthBook
  • 处理 trusted device enrollment
  • 检查 bypass / auto mode killswitch
  • 递增 authVersion

这说明登录不是结束,而是一次“身份切换后的全局再同步”。

7.2 /logout 也不是删本地 token 就完

登出前会先 flush telemetry,然后:

  • 清 API key
  • 清 secure storage
  • 清 auth cache
  • oauthAccount
  • 部分情况下清 onboarding 标记

这也是一次有秩序的 runtime shutdown。

8. org 强制登录校验:auth 和治理已经绑在一起

源码锚点:

  • src/utils/auth.ts

validateForceLoginOrg() 特别能说明这一层的定位。

它不是只看本地 token 里写了什么,而是会:

  • 先强制 refresh
  • 再去服务端拉权威 profile
  • 验证 forceLoginOrgUUID
  • 对无法确认 org 归属的情况默认失败

这代表组织策略不是“前端提示”,而是 auth runtime 的一部分。

9. Session Ingress:这是一条独立的访问链

源码锚点:

  • src/utils/sessionIngressAuth.ts
  • src/services/api/sessionIngress.ts

这一层非常容易被忽略。

它处理的不是普通模型调用,而是 session 日志和会话接入协议。

关键点包括:

  • CLAUDE_CODE_SESSION_ACCESS_TOKEN
  • 旧版 fd token
  • well-known file 回退
  • sk-ant-sid 场景下走 Cookie + org UUID
  • 其他场景走 Bearer

请求层还带着很强的会话一致性治理:

  • append 顺序写
  • x-last-uuid 处理并发冲突
  • 5xx / 429 / 网络错误重试
  • 401 立即失败

也就是说,Claude Code 不是把“登录凭证”和“会话接入凭证”混成一个概念。

10. 对 C# 拆分最有用的建议

如果后面做 C# 版,这一层最值得直接抽象成四个服务:

10.1 CredentialResolver

负责:

  • API key / OAuth token 来源优先级
  • managed context 隔离
  • provider 感知

10.2 OAuthSessionManager

负责:

  • PKCE 登录
  • 刷新
  • secure storage
  • cross-process invalidation

10.3 AuthPolicyGuard

负责:

  • org 强制校验
  • 受管会话限制
  • provider 与 auth 模式匹配校验

10.4 SessionIngressAuthenticator

负责:

  • 会话接入 token
  • header / cookie 生成
  • append/replay API 的认证细节

11. 一句话收口

Claude Code 的 auth 运行时,真正保护的不是“有没有登录”,而是“这个会话到底该以谁的身份、通过哪条受控链路去访问什么能力”。