# 管理凭据 QuantumAtlas 用 **PocketBase session token (JWT)** 和 **Personal Access Token (PAT)** 两种凭据。这一节讲实操:怎么拿、怎么存、CLI/CI 怎么用、轮换 / 撤销怎么做。 完整鉴权模型(authGuard / scopeGuard 等)见 [鉴权模型](../concepts/auth-model.md)。 ## 何时用 session 何时用 PAT | 场景 | 选哪个 | 理由 | |---|---|---| | 浏览器 SPA 里点点点 | session | 自动续期,开浏览器就有 | | `curl` / 单次脚本 | session 也行 | 14d 寿命对单次足够 | | **长跑 CLI / agent** | **PAT** | 365d 寿命;丢了能单独 revoke | | **CI workflow** | **PAT**,存 GitHub secret | scope 可裁剪,公开 repo 也不泄密 | | 写 PAT 管理脚本(`/api/pat`)| **session** | server 强制——leaked PAT 不能再 mint PAT | ## 创建 PAT ### CLI(推荐) 自 v0.18 起 `qatlas auth login` 走标准 OAuth Device-Code 流程,本机 / SSH 远端体验一致,不用手粘 token: ```bash qatlas auth login -s quantum-atlas.ai # 1) CLI 调 POST /api/oauth/device/code 拿一个 8 位 user_code + # 带 user_code 的深链 https:////device?user_code=WDJB-MJHT # 2) CLI 自动打开本机浏览器到那个深链(SSH / headless 用 --no-browser # 就只打印 URL,让你自己复制到任意有浏览器的设备) # 3) 浏览器侧若没登录会先走 GitHub OAuth;登录后看到 Authorize 表单, # 可以在浏览器里编辑 scope(默认全勾)/ 名称 / 过期天数,点 Approve # 4) CLI 每 5s 轮询 POST /api/oauth/device/token,approve 后立刻拿到 # 明文 PAT 写进 hosts.yml,结束 ``` 打印示例: ```text Logging into quantum-atlas.ai via https://quantum-atlas.ai. To finish logging in, open this URL in a browser: https://quantum-atlas.ai/device and enter the code: WDJB-MJHT (or open the deep link: https://quantum-atlas.ai/device?user_code=WDJB-MJHT) Waiting for approval (up to 600s, polling every 5s)… ✓ Logged in to quantum-atlas.ai Token: qat_abcd******** Name: qatlas-cli-laptop-2026-06-03 Scopes: papers:read, papers:write, theorems:read Expires: 2026-09-01 ``` flag(全部可选): | Flag | 默认 | 作用 | |---|---|---| | `--no-browser` | 关 | 不调 `webbrowser.open`,只打印 URL;headless / SSH 远端用 | | `--scopes a,b,c` | 空 | **预填**到浏览器表单的 scope 列表;空 = 默认全勾,用户可自行取消 | | `--expires-days N` | `90` | 预填的过期天数 (1–365);浏览器里可改 | | `--token-name NAME` | `qatlas-cli--` | 预填的 token 名;浏览器里可改 | | `--timeout SEC` | `600` | CLI 等浏览器 approve 的最大秒数 | | `--insecure` | 关 | 信任自签证书(如用 `tls internal` 的 IP 入口边缘) | | `--with-token` | — | CI 旁路:从 stdin 读 PAT(`cat token \| qatlas auth login -s --with-token`),跳过 OAuth 直接存进 hosts.yml;secret 不进 argv / shell history(跟 `gh auth login --with-token` 同款设计) | 所有 CLI 参数都只是**预填**,最终 token 的 name / scopes / expires 以浏览器里点 Approve 时表单上的值为准——浏览器始终是 source of truth。 ### device-code(SSH 远端 / 无 GUI 机器) 跟默认流程是同一条命令,只是加 `--no-browser` 让 CLI 别试着开本机浏览器: ```bash qatlas auth login --no-browser -H quantum-atlas.ai ``` 输出: ```text To finish logging in, open this URL in a browser: https://quantum-atlas.ai/device and enter the code: WDJB-MJHT (or open the deep link: https://quantum-atlas.ai/device?user_code=WDJB-MJHT) Waiting for approval (up to 600s, polling every 5s)… ``` 在**任何能开浏览器的设备**(手机、自己工位、隔壁同事的电脑都行)打开那个深链,按 Approve。CLI 这边轮询 `/api/oauth/device/token` 拿到 token 后自动写 hosts.yml。 用户 code 在 10 分钟内未 approve 会过期;过期了重跑命令即可。 ### 浏览器(自助创建 / 给别的工具用) 跟 `qatlas auth login` 走的是同一个 `/pat` 页面,区别只是没人通过 CLI 起 device-code flow: 1. 打开 `https:///pat` 2. 用 GitHub 登录(首次会问 OAuth 授权) 3. 点 **New token** 4. 填: - **Name**:人类可读,例如 `ci-upload-2026` - **Expires in days**:1–365 - **Scopes**:自 v0.18 起默认全勾,取消不想要的即可 5. 点 Create 6. **立即复制以 `qat_` 开头的明文**(不会再显示) 需要写进 client hosts.yml 让 CLI 用?`echo qat_xxx | qatlas auth login -s --with-token` 一条命令搞定。 ### server CLI(运维 / 救急) 在 server 主机上: ```bash qatlasd pat mint \ --user user@example.com \ --name "emergency-fix" \ --scopes papers:write,papers:read \ --expires-in-days 7 ``` 输出包含明文(仅此一次)。用于绕开 OAuth 故障时给指定用户发 token。 ## 存 PAT ### 本地长期(推荐) `qatlas auth login` 完成后自动落 `~/.config/qatlas/hosts.yml`(mode 0600)。之后所有 `qatlas` 命令针对该 host 自动用这个 PAT。 **支持多 host**: ```bash qatlas auth login -s atlas.example.com qatlas auth login -s atlas-edge2.example.com qatlas auth status # atlas.example.com # ✓ Logged in (stored at ~/.config/qatlas/hosts.yml) # - Token type: PAT # - Token value: qat_xxx******** # atlas-edge2.example.com # ... ``` ### CI / GitHub Actions repo Settings → Secrets → 加 `QATLAS_TOKEN` = PAT 明文,然后在 step 里用 `qatlas auth login --with-token` 从 stdin 注入 hosts.yml: ```{code-block} yaml :caption: .github/workflows/upload.yml - name: Configure qatlas env: QATLAS_TOKEN: ${{ secrets.QATLAS_TOKEN }} run: | uv tool install qatlas-cli qatlas config set server_url https://quantum-atlas.ai echo "$QATLAS_TOKEN" | qatlas auth login -s quantum-atlas.ai --with-token # 从 stdin 读,不进 history - name: Upload to QuantumAtlas run: | qatlas contrib pdf 2501.00010v1 --pdf paper.pdf --overwrite ``` > v0.17.0 起 client 不再读 `QATLAS_TOKEN` 等 env,**必须**经 `qatlas auth login --with-token` 中转到 hosts.yml。v0.19.0 还删了 `qatlas config set token` 路径(config.yaml `token:` 字段会静默盖 hosts.yml 里所有 per-host token,是 footgun)。 ## token 解析优先级 ```{mermaid} flowchart TD A[qatlas 命令] --> E{hosts.yml 有匹配 server_url 的条目?} E -->|有| H4[用 hosts.yml 的 stored token] E -->|无| H5[匿名请求] H5 --> F{是写口?} F -->|是| R401[401 Unauthorized] F -->|否| OK[正常读] ``` v0.19.0+:单层(hosts.yml > 匿名)。不再支持 `--token` flag / `QATLAS_TOKEN` env / config.yaml `token:` 字段。`server_url:` 字段(config.yaml)决定查 hosts.yml 时用哪个 host 当 key。 ## 通用 client flag 所有 `qatlas ` 共享的 flag: | Flag | 默认 | 作用 | |---|---|---| | `--request-timeout 120.0` | 120s | 单 HTTP 请求超时(per-call ergonomic)| > v0.17.0 删了 `--base-url` / `--token` / `--insecure` — server URL / TLS 选项必须写进 `~/.config/qatlas/config.yaml`(`server_url:` / `insecure:` 两个字段)。理由:client 是短命令多次调用,每次重复指 server 反而烦;YAML 单入口跟 `gh` / `kubectl` 主流认知一致。token 走 hosts.yml + `qatlas auth login` 单独管理。 ## 查 / 撤销 PAT ### 浏览器 `https:///pat` 列出全部 PAT,每行有 Revoke 按钮。点了**立即生效**(server 端 invalidate cache,单边缘 ms 级;跨边缘 60s 内)。 ### CLI(server 主机) ```bash # 列你管理的所有用户的所有 PAT qatlasd pat list # 列单一用户 qatlasd pat list --user user@example.com --json # 撤销 qatlasd pat revoke ``` ### API ```bash # 必须用 session token(PAT 不能改 PAT) curl -X DELETE https:///api/pat/ \ -H "Authorization: Bearer $SESSION_TOKEN" ``` ## 轮换 PAT 到期前一周收到提醒?批量轮换: ```bash # 旧 PAT 还能用 7d,先看现在哪些 host 用了 token qatlas auth status # 跑 login —— 自动开浏览器走 OAuth + Approve,生成的新 PAT 直接覆盖 # hosts.yml 里同 host 的旧条目;scope / 过期天数想改就在浏览器对话框里改 qatlas auth login -s quantum-atlas.ai --scopes papers:write --expires-days 90 # 验证新 PAT qatlas paper status 2501.00010v1 --kind markdown # 应该正常返回 # 旧 PAT 上 /pat 页面 revoke ``` CI 这边更新 GitHub secret 后下次 workflow 跑就用新的。 ## 常见陷阱 ```{admonition} 401 Unauthorized 但我配了 PAT :class: warning 检查 token 是否过期:`/pat` 页面看 expires_at。如果是 session token (JWT),14 天到期需要重登。 ``` ```{admonition} 403 insufficient scope: this token lacks papers:write :class: warning PAT 创建时没勾 `papers:write`。要么 revoke + 重建带 scope 的,要么用 session token。 ``` ```{admonition} 403 this endpoint requires a browser session token :class: warning 你在用 PAT 调 `/api/pat` —— **这条不通**。`/api/pat` 只接受 session token(浏览器登录后 `pb.authStore` 自动持有),有意为之防止 leaked PAT 自我复制。需要管理 PAT 请在浏览器 SPA 内打开 `/pat` 页操作。 ``` ```{admonition} 401 在另一台边缘用本机的 PAT :class: warning PocketBase 各边缘独立,PAT 不跨节点。每条线路需要分别在该边缘登录、各自 mint PAT。 ``` ## hosts.yml 长什么样 ```{code-block} yaml :caption: ~/.config/qatlas/hosts.yml hosts: atlas.example.com: token: qat_xxxxxxxxxxxxxxx added_at: "2026-05-29T01:23:45Z" atlas-edge2.example.com: token: qat_yyyyyyyyyyyyyyy added_at: "2026-05-29T01:23:50Z" ``` mode 600,归你。可以手 edit(但用 `qatlas auth login/logout` 更不容易出错)。 ## 退出登录 ```bash qatlas auth logout -s atlas.example.com # 删 hosts.yml 中此 host 条目(不调 server,只是本地清理) ``` 如果想真"撤销" PAT,去 `/pat` 页面 revoke。