DOCUMENTATION

测试身份适配指南

登录、权限、测试身份与用量接入

页面更新 2026-10-24

测试身份适配指南

更新时间:2026-09-30

本文档用于新的 subapp 适配 auth-center 的 Test Identity / 测试身份登录。

测试身份用于 agent、CLI、浏览器自动化和人工临时进入已发布 subapp 检查可用性。它不是普通用户账号,不进入 users 表,没有普通密码,也不会出现在普通登录页。

登录流程

  1. admin 在 auth-center dashboard 的 Test Access 创建测试身份。
  2. 创建、轮换或打开详情页时,dashboard 可复制完整 secret。
  3. agent 在终端用 name + secret + target_subapp 调用 /api/test-auth/exchange。
  4. auth-center 返回一次性 login_url。
  5. login_url 默认 60 秒内必须打开并消费。
  6. 消费成功后 auth-center 设置测试 session,并签发 JWT 跳转到目标 subapp。
  7. 测试 session 默认 30 分钟,可按测试身份配置调整。

注意:60 秒只限制一次性登录链接的消费时间,不限制测试时长。

Preview 浏览器入口

管理员可在 /dash -> Test Access 为测试身份开启 Preview。开启后,创建、轮换或详情页会提供:

https://accounts.aryuki.com/preview#name=<name>&secret=<secret>

人工 QA 可直接在浏览器打开该链接,点击 START 后从真实允许的应用列表进入目标 subapp。Preview 不替代 CLI:agent、CI 和脚本仍建议调用 /api/test-auth/exchange,取得一次性 login_url。

安全规则:

  • secret 只放在 URL fragment,不放 query string 或 pathname;Preview 页面会立即从地址栏删除 fragment 后才请求服务端。
  • Preview 创建独立 test_preview_session,该 HttpOnly、Secure、SameSite=Strict Cookie 仅限 /preview,不会覆盖普通 sso_session。
  • Preview 只列出身份 allowed_subapps 中真实 active 的 D1 apps;点击应用后会在新标签页使用既有 callback query JWT 流程进入 subapp,原卡片保留 2 秒启动反馈后恢复。subapp 无需新增 Preview 路由。
  • 关闭 Preview、禁用或删除测试身份会立即拒绝 Preview session;轮换 secret 只让旧链接失效,已存在的 session 可维持到其配置的过期时间或被管理员撤销。
  • 不要把 Preview 链接、secret、JWT 或 callback token 写入 Git、日志、公开聊天、前端代码或 issue。

Dashboard 管理入口

  • /dash → Test Access 用于创建、禁用、启用、删除和编辑测试身份。
  • 测试身份详情页路径为 /dev/@name,页面展示已放开的 app、读写权限、API per session、secret 复制入口和真实活动记录。
  • /dash → Register 中点击 register code 行会打开详情弹窗,可核对该 code 对应的 app 权限、cookie 有效期、使用状态和使用人。
  • 删除确认、敏感配置确认、测试身份编辑和 register code 详情弹窗均浮在 dashboard 顶栏之上;移动端打开后锁定底层页面滚动,仅滚动弹窗内容。

JWT 字段

测试身份 JWT 会继续使用 auth-center 的签名方式,旧 subapp 的 JWT 校验入口可以保持不变。

新增字段示例:

{
  "sub": "test_identity_id",
  "uuid": "test_identity_id",
  "username": "agent-ocr-checker",
  "role": "user",
  "identity_type": "test",
  "test_session": true,
  "allowed_subapps": ["ocr", "paste"],
  "data_scope": "public_read",
  "data_scope_permissions": ["public_read", "private_read"],
  "session_id": "test_session_id"
}

普通用户 JWT 不会包含 test_session=true。

旧 subapp 兼容策略

为了保证旧 subapp 可测试,未适配 data_scope 的旧 subapp 默认全可见。也就是说,只要旧 subapp 使用现有 /api/verify 校验 JWT,测试身份仍可进入。

新的 subapp 必须主动读取:

  • identity_type
  • test_session
  • allowed_subapps
  • data_scope
  • data_scope_permissions
  • session_id

并按下面规则限制 API。

data_scope 定义

data_scope_permissions 是 auth-center 展开后的等效权限数组。公共权限默认包含同级私有权限:例如 public_read 同时包含 private_read,但不包含 private_write。新 subapp 可以优先读取 data_scope_permissions,旧 subapp 也可以继续按 data_scope 判断。

public_read:Public read

可以读取公共数据和私人数据,不能写入公共数据或私人数据。等效权限:public_read、private_read。

公共数据包括:

  • subapp 全局配置
  • 公开页面配置
  • 公开模板
  • 公开资源
  • 公开列表
  • 公开统计
  • 公开状态
  • 非用户私有的系统信息

public_write:Public write

可以读取和写入公共数据,并包含私人读写权限。等效权限:public_read、public_write、private_read、private_write。

在此模式下,公共数据相关敏感 API 可以放开,例如:

  • 更新 subapp 全局配置
  • 修改公开模板
  • 触发公开数据同步
  • 重建公开索引
  • 刷新公开缓存
  • 修改公开资源

此模式风险较高,只应短期使用,并记录操作日志。

private_read:Private read

可以读取公共数据和私人数据,不能写入任何数据。

私人数据包括:

  • 真实用户内容
  • 用户文件
  • 用户邮件
  • 用户 paste
  • 用户 OCR 记录
  • 用户 API key 列表
  • 用户个人配置
  • 用户 usage 明细

private_write:Private write

可以读取和写入公共数据、私人数据。这是最高风险模式,只应在必要生产测试中短期使用。

中间件伪代码

function requireSubappAccess(payload, appId) {
  if (!payload) throw unauthorized();

  if (payload.identity_type === "test" || payload.test_session === true) {
    if (!payload.allowed_subapps?.includes(appId) && !payload.allowed_subapps?.includes("*")) {
      throw forbidden();
    }
    return {
      identityType: "test",
      subjectId: payload.sub,
      role: payload.role,
      dataScope: payload.data_scope || "public_read",
      dataScopePermissions: payload.data_scope_permissions || [payload.data_scope || "public_read"],
      sessionId: payload.session_id
    };
  }

  return {
    identityType: "user",
    subjectId: payload.sub || payload.uuid,
    role: payload.role || "user"
  };
}

function canReadPrivate(ctx) {
  if (ctx.identityType !== "test") return true;
  return (ctx.dataScopePermissions || []).includes("private_read");
}

function canWritePrivate(ctx) {
  if (ctx.identityType !== "test") return true;
  return (ctx.dataScopePermissions || []).includes("private_write");
}

function canWritePublic(ctx) {
  if (ctx.identityType !== "test") return true;
  return (ctx.dataScopePermissions || []).includes("public_write");
}

API 使用量上报

新的 subapp 可在测试身份请求结束后向 auth-center 上报用量:

POST /api/test-auth/usage
Authorization: Bearer <subapp_secret_key>
Content-Type: application/json
{
  "test_identity_id": "test_identity_id",
  "session_id": "test_session_id",
  "subapp": "ocr",
  "api_path": "/api/ocr/jobs",
  "method": "POST",
  "amount": 1,
  "status_code": 200,
  "metadata": {
    "route_group": "ocr"
  }
}

旧 subapp 如果仍使用 /api/quota/check 和 /api/quota/consume,auth-center 会对测试身份做兼容放行,并写入基础使用记录。

接入 checklist

  • JWT 校验仍使用 auth-center 现有公用逻辑。
  • 读取 identity_type=test 判断测试身份。
  • 使用 allowed_subapps 判断当前 app 是否允许访问。
  • 使用 data_scope 和 data_scope_permissions 限制 public/private scope。
  • 所有用户资产和业务数据仍使用 uuid/sub 作为稳定归属键。
  • 展示名称、username、email 只用于 UI,不作为数据归属键。
  • 对 private_write 做额外操作日志。
  • 上报 /api/test-auth/usage 以便 auth-center dashboard 查看测试活动。