docs: align MCP token flow to single ensure-only policy

This commit is contained in:
xqzp2026
2026-09-07 13:16:18 +09:30
parent 4d6dfa5025
commit 26769fb3be
3 changed files with 10 additions and 12 deletions
@@ -123,7 +123,7 @@ xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌──
```
- Claude Code:`claude mcp add --transport http xiaoqu https://mcp.<域名>/mcp --header "Authorization: Bearer <ak>"`。
- ak 明文只出现在受信任服务间响应和运行时;服务端保存 SHA-256 哈希及加密密文,含 ak 的客户端配置文件不得提交版本库。
- ak 明文只出现在服务端签发响应和运行时;服务端保存 SHA-256 哈希及加密密文,含 ak 的客户端配置文件不得提交版本库。
### 5.2 Web 管理后台(四个页面)
@@ -155,7 +155,7 @@ xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌──
- **`POST /api/open/token/ensure`**(get-or-create):
- 入参:`mobile`、`email`(至少一个,手机号优先)、`scopeCode`(作用域编码,必填)。
- 人员只认企业账号(`enterprise_type` 0/1,NULL 保留,与后台用户列表同口径);查不到 → 404。
- 该人员在该作用域下**已存在**有效 Token(正常且未过期)→ `created=false`,只回 `keyPrefix`/`expireTime` 等元信息;受信任应用可另调 `POST /api/open/token/get` 二次获取解密后的明文。
- 该人员在该作用域下**已存在**有效 Token(正常且未过期)→ `created=false`,只回 `keyPrefix`/`expireTime` 等元信息,明文不回传。
- **不存在** → 自动创建永久 Token(一作用域一 Token,memo 标记 `open-api 自动签发`),`created=true` 返回明文,仅此一次。
- 全程写 `audit_log`(`tool_name=open.token.ensure`,`client_type=open-api`,参数摘要脱敏)。
@@ -240,7 +240,7 @@ xiaoqu-agent-service/
- 不依赖老 jar,Entity 自行生成 → 与老系统零代码依赖
- 前后端一体打包单 jar → 可逆决定,将来可拆
- 一作用域一 Token(2026-09-04)→ 收回某能力不需整把吊销;数据模型不变(token_scope 每把只写一行)
- 长期 Token 需要二次获取 → 服务端保存哈希和加密密文,`token/get` 仅对白名单服务开放并全程审计
- 长期 Token 仅按用户首次创建返回明文一次;后续复用元信息,不对外提供二次获取明文接口(服务端仅用于禁用/审计与治理)
- Open API 鉴权用配置文件静态密钥(dev)→ 服务间调用方少,落库+轮换留给生产阶段
- 列表类接口一律服务端分页,pageSize/rows 上限 100(超出截断为 100)→ 防止一次性拉全表,MCP 工具与后台 API 同一尺度
@@ -67,7 +67,7 @@
| MCP Token 过期/401 | 清除当前用户 MCP Token,重新换取一次;同一请求不自动重放工具副作用 |
| accessToken 刷新 | 保留 MCP Token;若服务返回 401,再重新换取 |
| 切换账号 | 先断开旧 MCP Client,再加载新账号 Token 并建立新 Client |
| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;当前永久 Token 不会自动失效,需要显式调用 revoke |
| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;当前永久 Token 以服务端状态为准,不做客户端侧远程失效调用 |
| 多账号 | Keychain key 必须包含稳定用户 ID,禁止所有账号共用 `mcpToken` |
~~建议 MCP Token 默认有效期 15 分钟,并提供换取接口;长期 AK 仅用于受控开发/管理场景。~~
@@ -210,12 +210,12 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
| 文件/目录 | 层级 | 类型 | 改动说明 |
|---|---|---|---|
| ~~`security/` JWT 验证组件~~ | Security | ~~新增~~ 取消 | agent-service 不验证 xiaoqu-ai 登录态,改由业务后端两段式调用 |
| `open/OpenTokenController.java` 等 | Open API | **ensure 已交付;get 待实现** | `ensure`、`get`、`rotate`、`revoke` 均使用 X-Open-Key 鉴权 |
| `open/OpenTokenController.java` 等 | Open API | **ensure 已交付** | `ensure` 使用 X-Open-Key 鉴权 |
| `open/OpenKeyFilter.java` | Filter | **已交付** | 服务间静态密钥,常量时间比较,配置为空即关闭 |
| `admin/AdminTokenService.java` | Service | **ensure 已交付;密文保存待实现** | 一作用域一 Token;签发时保存哈希和加密密文 |
| `common/GlobalExceptionHandler.java` | Handler | **已交付** | 错误统一 `{"message": ...}` |
| `security/AkAuthFilter.java` | Filter | 保持兼容 | `/mcp` Bearer ak 校验不变 |
| `src/test/...open` | Test | **部分已交付** | 增加 get/密文解密失败/密钥轮换/权限白名单测试 |
| `src/test/...open` | Test | **部分已交付** | 覆盖 ensure 的参数、已存在/新建/404/停用/缺参测试 |
### xiaoqu-ai
@@ -235,7 +235,7 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|---|---|---:|---|---|
| 1 | ~~服务端确认 JWT 与租户映射、Token exchange 契约~~ → xiaoqu-ai 业务后端实现门面接口(登录态 → ensure) | 2h | - | 门面 401/403 语义确定;明文按 user_id 加密落库 |
| 2 | ~~服务端实现用户 MCP Token 签发与审计~~ **已交付**(ensure + 审计,2026-09-04) | - | - | 已上线并实测幂等/脱敏/错误语义 |
| 2b | 服务端增加 Token 加密保存与二次获取 | 3h | #2 | `get` 仅受信任应用可调用;加密密文可解密;明文不落日志 |
| 2b | ~~服务端增加二次获取能力~~ | - | - | 已按当前边界关闭,不再新增接口 |
| 3 | iOS Keychain 账号隔离和认证生命周期接入 | 3h | #1 | 登录/切换/退出无旧 Token 残留 |
| 4 | Rust MCP HTTP Bearer、Session、初始化通知 | 3h | #3 | initialize/list/call 真实握手成功 |
| 5 | MCP 工具到 Server 路由映射 | 2h | #4 | 多 Server 下工具调用不发错目标 |
@@ -45,7 +45,7 @@ Content-Type: application/json
- `X-Open-Key` 只能配置在受信任后端,不能下发 App。
- 业务后端必须从已验证用户会话查询手机号/邮箱,不能信任客户端或模型传入的身份字段。
- 首次创建返回 Token 明文;xiaoqu-mcp 同时保存 SHA-256 哈希和 KMS/独立密钥加密密文。
- 长期 Token 可由受信任业务后端通过 `POST /api/open/token/get` 二次获取;该接口不能下发 App 或暴露给模型。
- 已存在 Token 不回传明文,重复调用返回元信息并复用。
- Token 密文缺失或解密失败必须报错;不得回退到哈希值或公共 Token。
## 4. 推荐实现
@@ -59,7 +59,6 @@ Content-Type: application/json
| `XQ_MCP_ENABLED` | `false` | 是否启用 MCP capability |
| `XQ_MCP_URL` | unset | 例如 `http://xiaoqu-mcp:8088`,只存地址,不存用户 Token |
| `XQ_MCP_OPEN_KEY` | unset | 仅用于服务端 Token 初始化,使用 `SecretStr` |
| `XQ_MCP_TOKEN_GET_ENABLED` | `false` | 是否允许业务后端二次获取已有 Token |
| `XQ_MCP_SCOPE_CODE` | `operation` | 固定作用域,禁止由 Planner 或客户端选择 |
| `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 |
@@ -75,7 +74,7 @@ Content-Type: application/json
- `src/ops_assistant_service/mcp_token_provider.py`
- 接收已验证的 `SharedIdentity`。
- 从受信任用户资料源取得 mobile/email。
- 调用 `ensure`,首次创建时由 xiaoqu-mcp 加密保存;已有 Token 通过受控 `get` 获取。
- 调用 `ensure`,首次签发返回明文;后续直接复用本地持久化 token。
- 不接受 `user_id/mobile/email` 作为客户端覆盖参数。
不引入通用 MCP 框架、第二个 Agent 或数据库直连;现有 `httpx` 已足够完成首期只读接入。
@@ -118,8 +117,7 @@ validate_tool_input
- 登录用户变化:先关闭旧 Session,再建立新用户 Session。
- MCP 返回 401:清理当前用户 Token,最多执行一次重新获取和重连;不自动重放副作用请求。
- 退出登录:清理本地/服务端 Token 缓存;实现 revoke 后再主动撤销远端 Token。
- 长期 Token 二次获取:仅由白名单业务后端调用 `token/get`,按用户和 scope 审计,不缓存响应。
- 退出登录:清理本地/会话 Token 缓存;如需远端立即失效,走服务端管理端吊销能力。
- 日志只记录用户 ID、工具名、scope 和错误码,不记录 Token、Authorization Header 或手机号/邮箱明文。
- `tools/list` 仅用于校验远端能力;真正可调用集合以本地 allowlist 为准。