oclaw/uds-auth/docs/skill-auth-standard.zh.md
oliver 0e6de055a9
Some checks failed
ci / test (push) Has been cancelled
ci / test-postgresql (push) Has been cancelled
fix(uds-auth): keep sessions until manual sign-out; refresh expired UAC tokens by QR (0.3.20)
Users were asked to sign in again every ~30 minutes, even while active.

Session lifetime
- Browser: UDS_SESSION kept its login-time Max-Age and was never re-issued,
  so the cookie vanished 30 min after login. Re-issue it on use (throttled
  by slidingInterval).
- Desktop: remote.mux WS traffic never passed the HTTP auth middleware, so
  the idle TTL never slid. assertPrincipalLive now reports activity and
  slides the session and the account's bridges (throttled).
- New setting sessionIdleTimeoutHours (Settings page + config). Default 0 =
  no automatic sign-out; only a manual sign-out ends a session, so
  conversations and night-time cron are never cut off. 0.5-168 enables an
  idle timeout with the 7-day cap. The TTL is read live.
- Stores support long TTLs: the absolute cap follows the TTL, older 7-day
  sessions are lifted on activity, and expiry timers re-arm instead of
  overflowing setTimeout (~24.8 days).

UAC token check
- Every 2 hours (and 1 min after start) each QR session's token is checked
  against icenterapi. A rejected token is only flagged, never a sign-out.
  Network errors, 5xx and bad JSON keep the previous verdict (the old 6h
  check revoked all of an account's sessions on any failure, including
  outages).
- GET /uds-auth/session-status (non-sliding) returns alive + tokenValid.
  The client probes it every minute and on focus: a gone session signs the
  UI out with a notice; a rejected token opens a refresh QR once.
- Scanning in refresh mode writes the new token into every live session of
  the account and the skill credential cache in place: no new session, no
  reload. Other accounts are rejected (token_refresh_emp_mismatch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:59:48 +08:00

131 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Skill 认证标准(uds-auth)
面向 DeepSeekHarness:现场安装 **uds-auth 插件**(内含 skill `uds-skill-auth`)后,第三方 / 自研 skill 按本标准取凭证或出站调用。
**转发给改造方时**:发整个 `uds-auth` 插件即可;公共 skill 在 [`skills/uds-skill-auth/`](../skills/uds-skill-auth/),说明见 [uds-skill-auth.zh.md](./uds-skill-auth.zh.md)。
## 现场形态
```text
安装 uds-auth 插件(内含 skills/uds-skill-auth)
→ 将 uds-skill-auth 加入 skills 路径(或复制到工作区 skills/)
→ 用户扫码登录
→ 安装/加载业务 skill
→ 可用
```
无需再配全局 `coclaw_token` / `AUTH_VALUE`。
**认证公共库随插件分发**:路径 `uds-auth/skills/uds-skill-auth/`。
## 双轨模型
| 轨道 | 用途 | 行为 |
|------|------|------|
| UI | Cookie + `sessionStore` + `/api/me` | **保持原样**;logout 仍清 cookie 并 `sessionStore.delete` |
| Skill | `skillCredentialCache`(`skill-credentials.json`) | 登录成功时写入/刷新;供 agent / cron |
配置项:
- `retainSkillCredentialsOnLogout`(默认 `true`):UI 退出时是否保留 skill token
- `skillCredentialTtlSeconds`(默认 `604800` = 7 天)
- `outboundAllowedHosts`:出站白名单(逗号分隔)
管理员删除用户会同时清掉该用户的 skill 凭证。
## Agent 环境
Shell / 任务 env(非密钥除非 Host 显式注入 capability):
- `DSH_SESSION_ID` — 当前 agent 会话
- `DSH_WEB_URL` — 可选,用于拼 Host 地址
- `UDS_TASK_CAPABILITY` — **推荐**:Host/`mintTaskCapability` 签发的短期令牌(scopes: `outbound` 和/或 `credentials`)
Host 在 `session/created` 时会 `stampSessionOwner(sessionId, empNo)`。Cron 可按 `ownerEmpNo` 解析并 mint capability;**不要**依赖浏览器 Cookie。
账号停用/删除会撤销该用户全部 task capability。
## 两种合法模式
### Mode Outbound(推荐新 skill)
```http
POST /uds-auth/outbound
X-DSH-Session-Id: <DSH_SESSION_ID>
X-UDS-Task-Capability: <UDS_TASK_CAPABILITY> # scope 须含 outbound
{ "url": "https://icenterapi.zte.com.cn/...", "method": "POST", "headers": {...}, "body": {...} }
```
- 仅 **loopback**
- Host 注入 `X-Emp-No` / `X-Auth-Value`
- 剥离客户端自带鉴权头
- host 必须在白名单内;URL 必须 **HTTPS**
- capability 仅 `credentials` scope 时 **不能** 调 outbound
Python(先把 `uds-skill-auth/scripts` 加入 `sys.path`):
```python
from uds_skill_auth import request
out = request("POST", url, headers={...}, body={...})
# out["statusCode"], out["json"], out["body"]
```
### Mode Creds(必须自管 HTTP 时)
默认 `allowRawAgentToken=false`。开启后:
```http
POST /uds-auth/agent-credentials
X-DSH-Session-Id: <DSH_SESSION_ID>
X-UDS-Task-Capability: <UDS_TASK_CAPABILITY> # scope 须含 credentials
```
返回 `{ empNo, token }`。可设进程内 `EMP_NO` / `AUTH_VALUE` 别名,**禁止**写进 SKILL.md 教模型 `printenv`。仅 `outbound` scope 的 capability **不能**取 raw token。
```python
from uds_skill_auth import resolve
# 优先用 request()/outbound;仅在 allowRawAgentToken=true 时使用 resolve
creds = resolve(apply_env_aliases=False)
```
Helpers 来源:插件内 skill `uds-auth/skills/uds-skill-auth/scripts/`(装到 skills 路径后,或环境变量 `UDS_AUTH_HELPERS`)。
## 改造现有 Skill 清单
1. 现场确保已从插件安装 `uds-skill-auth`(`uds-auth/skills/uds-skill-auth`)
2. 业务脚本定位 sibling:`.../skills/uds-skill-auth/scripts` → `import uds_skill_auth`
3. 替换原来的 `coclaw_*` / `EMP_NO`/`AUTH_VALUE` 手工读环境:改用 `resolve()` 或 `request()`
4. SKILL.md 只写「需已 UDS 登录,并已安装 uds-skill-auth」,不写 token 变量名
5. JSON stdout;日志脱敏
定位示例:
```python
from pathlib import Path
import sys
def load_uds_skill_auth():
here = Path(__file__).resolve().parent
candidate = here.parents[1] / "uds-skill-auth" / "scripts"
if not (candidate / "uds_skill_auth.py").is_file():
raise RuntimeError("请先安装 uds-skill-auth skill")
sys.path.insert(0, str(candidate))
import uds_skill_auth
return uds_skill_auth
```
## 禁止项
- 全局共享 token / 把 token 注入 `shellEnv`
- SKILL 教打印环境变量中的密钥
- 无白名单的开放代理
- 用 UI `sessionStore` 的闲置 TTL(`sessionIdleTimeoutHours`)冒充 skill 长凭证
- 业务 skill 硬编码插件源码路径(应用 `uds-skill-auth` skill)
## 验收
- UI 登录/退出与改前一致
- 默认配置下 UI 退出后 skill/cron 仍可用
- `retainSkillCredentialsOnLogout=false` 时退出后取证失败
- 未安装 `uds-skill-auth` 时业务 skill 报错清晰
- 两会话不串号;未登录错误可理解且无 token 泄露