第三十五章:认证运行时真正隔离的是什么
拆 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.tssrc/utils/sessionIngressAuth.tssrc/services/oauth/index.tssrc/services/oauth/client.tssrc/commands/login/login.tsxsrc/commands/logout/logout.tsxsrc/services/api/sessionIngress.ts
2. 先说结论
我对这一块的判断是:
Claude Code 的认证层主要在做三件事:认证来源仲裁、宿主边界隔离、组织策略校验。
最关键的几个结论是:
- first-party OAuth 和第三方 provider 不是一套凭证链。
- managed context 会故意阻止回退到用户本地 API key。
/login和/logout本质上是 runtime rebind,不只是改一个 token。- 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.tssrc/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.tsxsrc/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.tssrc/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 运行时,真正保护的不是“有没有登录”,而是“这个会话到底该以谁的身份、通过哪条受控链路去访问什么能力”。