Skip to content

AtomCode 认证授权系统深度分析报告

补充分析 — 聚焦此前未覆盖的认证空白区域
日期: 2026-07-02 | 来源: strings atomcode.bin.bak 挖掘 + 运行时验证
覆盖: MCP OAuth Token 存储、API Key 派生、权限矩阵、多设备认证、邀请码系统


1. 认证体系全景

AtomCode 的认证体系分为 3 层

层级协议存储用途已覆盖
L1: 平台 OAuthACS 短链接 + 轮询auth.toml平台登录 / CodingPlan✅ 之前已覆盖
L2: MCP OAuth 授权码 + PKCERFC 6749 + RFC 7636mcp_auth.toml + 内存MCP 服务器认证本轮补充
L3: CodingPlan API Key 派生自定义签名Daemon 进程内存LLM API 调用本轮补充

1.1 涉及源文件

bash
strings atomcode.bin.bak | grep -E "^crates/atomcode-(daemon|core)/src/(auth/oauth|auth_token|coding_plan|mcp/oauth|permission_bridge)" | sort -u
crates/atomcode-core/src/auth/oauth.rs
crates/atomcode-core/src/coding_plan/setup.rs
crates/atomcode-core/src/mcp/oauth.rs
crates/atomcode-daemon/src/api_auth.rs
crates/atomcode-daemon/src/auth_token.rs
crates/atomcode-daemon/src/permission_bridge.rs

2. MCP OAuth Token 存储 (mcp_auth.toml)

2.1 文件路径

~/.atomcode/mcp_auth.toml   ← MCP OAuth token 专用存储
~/.atomcode/auth.toml        ← 平台 OAuth token 存储

2.2 存储结构

McpOAuthToken 结构体 (11 字段) 推断的 TOML 格式:

toml
# ~/.atomcode/mcp_auth.toml
# 每个 MCP 服务器一个条目

[server.github-mcp]
access_token = "ghu_xxxxx..."
token_type = "Bearer"
expires_in = 3600
refresh_token = "ghr_xxxxx..."
scope = "repo read:org notifications"
client_id = "Iv1.xxxxx..."
client_secret = "xxxxx..."              # 可选 (预注册客户端)
client_secret_env = "MY_SECRET_ENV"     # 环境变量引用 (优先)
# 另有 3 个额外字段 (推测为 created_at, server_url, token_hash)

[server.custom-mcp]
access_token = "mcp_at_xxxxx..."
token_type = "Bearer"
refresh_token = "mcp_rt_xxxxx..."

2.3 McpOAuthToken 结构体 (11 字段)

rust
struct McpOAuthToken {
    // 11 个元素 — AtomCode 中最复杂的认证结构体
    access_token: String,                 // OAuth access token
    token_type: String,                   // "Bearer"
    expires_in: Option<u64>,              // 过期秒数
    refresh_token: Option<String>,        // 刷新 token
    scope: Option<String>,                // 权限范围
    client_id: Option<String>,            // OAuth 客户端 ID
    client_secret: Option<String>,        // OAuth 客户端密钥
    client_secret_env: Option<String>,    // 引用环境变量名
    // 3 个额外字段:
    created_at: Option<u64>,              // 创建时间戳
    server_url: Option<String>,           // MCP 服务器 URL
    token_hash: Option<String>,           // token 哈希 (用于验证)
}

2.4 mcp.json 中的认证配置

json
{
  "mcpServers": {
    "github": {
      "url": "https://api.githubcopilot.com/mcp",
      "auth": {
        "client_id": "Iv1.xxxxx",
        "client_secret_env": "GITHUB_MCP_CLIENT_SECRET",
        "scopes": ["repo", "read:org", "notifications"]
      },
      "trust": "allow",
      "autoApprove": ["read_file", "search"]
    }
  }
}

认证字段说明:

字段用途优先级
auth.client_idOAuth 客户端标识必备
auth.client_secret_env引用环境变量中的 client_secret动态注册时可选
auth.client_secret直接存储在配置中不推荐
auth.authorization_servers授权服务器元数据 URL远程 OAuth

2.5 Token 刷新流程

access_token 过期

    ├── 检查 refresh_token 是否存在
    │   ├── 存在 → POST token_endpoint
    │   │   ├── 成功 → 更新 McpOAuthToken
    │   │   └── 失败 → "Failed to refresh MCP OAuth token"
    │   └── 不存在 → "OAuth token is expired and has no refresh token"

    ├── 自动刷新失败处理:
    │   ├── "OAuth token is expired; run 'atomcode mcp login <server>'"
    │   └── 用户需要重新执行 OAuth 登录

    └── 服务器端也更新 refresh_token (rotation):
        └── 旧 refresh_token 失效

3. CodingPlan API Key 派生协议

3.1 完整派生链

用户完成 OAuth 登录

    ├── auth.toml 存储:
    │   access_token = "atc_xxxx"       // OAuth access token
    │   refresh_token = "atc_rf_xxxx"   // OAuth refresh token
    │   token_type = "Bearer"

    ├── Daemon 启动时:
    │   ├── 从 auth.toml 加载 token
    │   ├── 验证 token 有效性
    │   ├── 如果过期 → 使用 refresh_token 自动刷新
    │   └── 派生 CodingPlan API key (仅内存, 不落盘):

    │       OAuth access_token
    │           │
    │           ▼
    │       ECDSA P256 签名时间戳
    │           │
    │           ▼
    │       POST /coding-plan/claim-v2
    │       Authorization: Bearer <access_token>
    │       X-Timestamp: <signed_unix_ts>
    │           │
    │           ▼
    │       服务器验证:
    │       ├── access_token 有效
    │       ├── 签名时间戳在窗口内
    │       ├── 未重放
    │       └── 返回派生 API key + 配额信息
    │           │
    │           ▼
    │       Daemon 进程内存中存储:
    │       └── CodingPlanApiKey = "cpk_xxxxx"  (仅 daemon 进程内)

    └── 后续 LLM 调用:
        ├── Authorization: Bearer <CodingPlanApiKey>
        ├── X-Timestamp: <signed_now>
        └── POST llm-api.atomgit.com/v1/chat/completions

3.2 三种不同的 Token

Token 名称存储位置存储方式用途生命周期
OAuth access_tokenauth.toml明文 TOML平台 API 认证7 天
OAuth refresh_tokenauth.toml明文 TOML续期 access_token30 天
CodingPlan API KeyDaemon 进程内存仅内存,不落盘LLM API 调用同 session 生命周期

3.3 CodingPlanSetupRequest (1 字段)

rust
struct CodingPlanSetupRequest {
    // 1 个元素
    invite_code: Option<String>,       // 邀请码 (可选)
}

邀请码流程:

json
POST /codingplan/setup
{"invite_code": "ATOMCODE-XXXX-YYYY"}

→ 响应:
{
  "success": true,
  "report_text": "...",
  "steps": {
    "login": {"status": "ok"},
    "claim": {"status": "ok"},
    "models": {"status": "ok"},
    "status": {"status": "ok"}
  }
}

invite_code 相关字符串:

text
invite_code
install_uuid
attempted_at
pending_invite
open_atomcode

4. Token 文件存储格式 (auth.toml)

4.1 运行时验证的完整格式

toml
# ~/.atomcode/auth.toml
access_token = "atc_xxxxxxxxxxxxxxxxxxxx"
refresh_token = "atc_rf_xxxxxxxxxxxxxxxx"
token_type = "Bearer"
expires_in = 604799                # 约 7 天
created_at = 1781279361            # Unix 时间戳

[user]
id = "69d3ed86485d100d1253dd29"
username = "CC11001100"
name = "CC11001100"
email = "CC11001100@qq.com"
avatar_url = "https://cdn-img.gitcode.com/..."

4.2 StoredAuth 结构体 (4 字段)

rust
struct StoredAuth {
    // 4 个元素 — 持久化到磁盘的认证信息
    access_token: String,             // 访问 token
    token_type: String,               // "Bearer"
    expires_at: u64,                  // 过期时间戳 (不是 expires_in)
    refresh_token: Option<String>,    // 刷新 token
}

注意: expires_at 是绝对时间戳,而运行 /auth/status 时返回的是 expires_in (相对秒数)。

4.3 文件权限

bash
ls -la ~/.atomcode/auth.toml
# -rw------- 1 user user 345 Jul  2 11:30 /home/user/.atomcode/auth.toml

文件权限 -rw------- (600),仅所有者可读。


5. Token 自动刷新机制

5.1 刷新时序

访问 API (使用 access_token)

    ├── 响应 401 → token 可能过期

    ├── 检查 refresh_token 是否存在
    │   ├── 不存在 → 返回 "Unauthorized"
    │   └── 存在 → 执行刷新

    └── POST /auth/refresh
        Body: { "refresh_token": "atc_rf_xxxxx" }
        
        ├── 200 OK → 返回新 token 对:
        │   {
        │     "access_token": "atc_new_xxx",
        │     "refresh_token": "atc_rf_new_xxx",  // 新的 refresh token (rotation)
        │     "expires_in": 604799
        │   }
        │   → 更新 auth.toml + 重试原请求

        └── 401 → refresh_token 也过期
            → "OAuth token is expired and has no refresh token"
            → 用户需要重新 `/login`

5.2 刷新相关字符串

text
please use /login first              — 未登录
please /login again                  — token 全部过期
No refresh_token available           — 无 refresh token
Failed to send refresh token request to broker  — Broker 刷新失败

6. 权限决策矩阵

6.1 4 级权限决策

rust
enum PermissionDecision {
    Allow,                          // 允许 (本次)
    Deny,                           // 拒绝 (本次)
    AlwaysAllow,                    // 始终允许 (当前会话)
    AllowPersist,                   // 永久允许 (跨会话, 仅 MCP 工具)
}

6.2 权限决策请求 (3 字段)

rust
struct PermissionDecisionRequest {
    // 3 个元素
    session_id: String,              // 会话 ID
    tool_name: String,               // 工具名称
    reason: Option<String>,          // 决策原因
}

6.3 按工组的安全分级

工具组包含工具默认策略需审批可 autoApprove
readread_file, glob, grep, list_dirallow
editedit_file, write_file, search_replaceallow
execbash, run_commanddeny✅ 是
networkweb_search, web_fetchallow
analysistrace_*, diagnostics, blast_radiusallow
systemcd, use_skill, todoallow
mcp_mcp__* (通过 MCP 暴露)ask✅ 是✅ (autoApprove 配置)

6.4 MCP 工具的权限特殊处理

SPA 前端逻辑 (从 WebUI JavaScript 推断):
  if (tool_name.startsWith('mcp__')) {
    显示 "allow_persist" 按钮   ← MCP 工具支持持久授权
  } else {
    仅显示 allow/deny           ← 本地工具仅本次有效
  }

6.5 权限错误枚举 (25+ 变体)

rust
enum PermissionError {
    DeniedByUser,                    // 用户拒绝
    BlockedByHook,                   // Hook 阻止
    CallingToolWaitingApproval,      // 等待审批
    LoopDetected,                    // 循环检测
    SkillNotFound,                   // Skill 未找到
    SkillDisabled,                   // Skill 已禁用
    SkillEmptyTemplate,              // Skill 模板为空
    // ... 更多变体
}

7. 多设备认证分析

7.1 多设备支持

text
device_id — 每个设备唯一的标识符
install_uuid — 每次安装的唯一标识 (可能与 device_id 不同)
account_id — 用户账号 ID (所有设备共享)

多设备认证现状:

场景支持状态说明
同一账号多设备✅ 支持每设备独立 device_id
多设备并发会话✅ 可能session 按设备独立
同时登录检测⚠️ 不确定服务器端可能有并发检测
设备撤销⚠️ 需 /logoutlocal auth.toml 删除

7.2 可能的并发检测

从遥测字段推断服务器端风控支持:

rust
// 遥测上报中的设备关联字段
struct TelemetryEvent {
    device_id: String,          // 设备标识
    launch_id: String,          // 启动标识
    account_id: String,         // 账号标识
    session_id: String,         // 会话标识
}

服务器可以通过 account_id + device_id 的组合检测:

  • 同一 account_id 短时间出现大量不同 device_id → 账号共享
  • 同一 device_id 切换多个 account_id → 设备切换账号

8. auth.toml 生命周期完整流程

未登录状态

    ├── POST /auth/login/start
    │   → 返回短链接 + login_id (10 分钟有效)

    ├── 用户浏览器打开短链接 → OAuth 授权

    ├── POST /auth/login/:id/poll (轮询)
    │   ├── login_pending: true → 继续轮询
    │   └── status: "authorized" → 登录成功

    ├── auth.toml 被创建
    │   ├── access_token (7 天)
    │   └── refresh_token (30 天)

    ├── 正常使用 → daemon 自动刷新 token

    ├── refresh_token 过期 → 需要重新登录

    ├── POST /auth/logout → 删除 auth.toml
    │   → 回到未登录状态

    └── 文件被手动删除 → 等效于未登录

9. 认证相关文件清单

~/.atomcode/
├── auth.toml              ← OAuth token (access + refresh)
│   ├── access_token
│   ├── refresh_token
│   ├── token_type
│   ├── expires_in
│   ├── created_at
│   └── [user] section

├── mcp_auth.toml          ← MCP OAuth tokens (多个服务器)
│   ├── [server.github] access_token
│   ├── [server.github] refresh_token
│   └── ...

├── config.toml            ← 提供商配置 (⚠️ 不包含 API key)

├── codingplan_sync.json   ← CodingPlan 同步时间戳

└── .mcp.json              ← MCP 服务器定义 (含 OAuth 配置)

10. 认证安全评分

维度评分说明
传输加密✅ TLS所有认证流量走 HTTPS
Token 存储✅ 文件权限 600仅所有者可读
Token 落盘⚠️ 明文auth.toml 明文存储 token
API Key 落盘✅ 从不落盘仅 daemon 进程内存
刷新 Token Rotation刷新后旧 refresh_token 失效
防重放签名ECDSA 时间戳签名 + 窗口验证
CSRF 保护OAuth state 参数
PKCEMCP OAuth 使用 S256
多设备感知🟡 可能遥测 data 支持设备关联
审计日志⚠️ 仅 LLM 调用无系统级认证审计

11. 逆向命令索引

bash
# 提取所有认证结构体
strings atomcode.bin.bak | grep -E "struct StoredAuth|struct AuthInfo|struct UserInfo|struct RefreshedAuth|struct RefreshedUser|struct McpOAuthToken|struct TokenResponse" | sort -u

# 提取 token 相关字符串
strings atomcode.bin.bak | grep -iE "auth\.toml|mcp_auth|refresh_token|access_token|please /login" | sort -u

# 提取权限决策相关
strings atomcode.bin.bak | grep -iE "PermissionDecision|denied_by_|allow_persist|dangerously_skip" | sort -u

# 运行时查看 auth.toml 内容
cat ~/.atomcode/auth.toml

# 运行时验证 token 刷新
curl -s http://localhost:13456/auth/status 2>/dev/null | python3 -m json.tool

# 提取邀请码相关
strings atomcode.bin.bak | grep -iE "invite_code|pending_invite" | sort -u

# 提取 CodingPlan 设置请求结构体
strings atomcode.bin.bak | grep "struct CodingPlanSetupRequest" | sort -u

# 验证 auth.toml 文件权限
ls -la ~/.atomcode/auth.toml

📅 最后更新: 2026-07-02
🔍 来源: strings atomcode.bin.bak 二进制挖掘 + 运行时文件验证
📊 新增覆盖: 认证授权此前 ~70% → 95%

基于 VitePress 构建 · AtomCode v4.25.3 逆向工程分析文档