原生声明与 Errand
Steam ticket、AccessKey 与 PublicKey direct-issue 不经过应用自己的登录页面。这让成功路径非常短,但也意味着客户端无法展示同意界面,或要求用户当场补齐个人资料。
本页是各类凭据 Errand 恢复流程的唯一文字参考,包括轮询与 direct-issue 重试证明。
本页同时处理这个限制的两面:
- 为非交互式客户端选择合适的声明策略。
- 当 required 真实声明无法签发时,处理 Errand 浏览器交接。
共享的策略与授权模型见身份声明与共享。
选择原生声明策略
Section titled “选择原生声明策略”| 策略 | 原生 direct-issue 行为 |
|---|---|
| Off | 从不请求。 |
| Optional | 仅在用户此前已经授权时共享。永不阻塞,因此纯原生用户可能永远不会被提示。 |
| Required | 保证使用真实数据且一定存在。缺少同意或数据时返回带 Errand 的 403。 |
仅占位资料 (SYNTHETIC_ONLY) |
保证以稳定占位值存在。从不请求真实数据,永不阻塞,也不会创建 Errand。 |
占位兜底 (SYNTHETIC_FALLBACK) |
保证存在;已授权时使用真实数据,否则使用稳定占位值。永不阻塞,也不会创建 Errand。 |
对大多数原生集成,除非你明确需要经过验证的真实数据,否则应优先选择 SYNTHETIC_ONLY 或 SYNTHETIC_FALLBACK,而不是 REQUIRED。
- 只需要稳定的姓名或邮箱形态值,可以直接使用占位:使用
SYNTHETIC_ONLY。 - 想让用户可选择分享真实数据,但 direct-issue 在未授权时仍用占位继续:使用
SYNTHETIC_FALLBACK。 - 需要真实、已验证的邮箱用于送达或外部对账:使用
REQUIRED并实现 Errand 流程。 - 用户已经授权时希望拿到真实数据,但缺少时仍可继续:使用
OPTIONAL。
如果所有请求的声明都是 OFF、SYNTHETIC_ONLY 或 SYNTHETIC_FALLBACK,声明策略就永远不会迫使 direct-issue 进入浏览器交接。
Synthetic 姓名是生成的占位值;Synthetic 邮箱使用稳定的 …@proxy.sudomimus.email 地址;Synthetic 头像使用生成的扇区头像图片。代理投递仅为尽力而为,不保证送达;OIDC 会把 synthetic 邮箱标记为 email_verified: false。
头像 URL 的隔离、轮换与缓存行为见头像声明与交付。
Errand
Section titled “Errand”Errand 是短暂的账户补救流程,不负责签发令牌。当 direct-issue 无法满足 required 声明时,403 响应会向客户端提供一个浏览器 URL。用户在那里完成同意或补资料,随后客户端按下文的凭据规则准备证明,重新请求直接签发。
只有两种声明把关原因会携带 Errand:
403 reason |
含义 | 浏览器内的工作 |
|---|---|---|
ClaimConsentRequired |
required 声明尚未获得授权。 | 授予同意;必要时先补齐缺失数据。 |
RequiredClaimDataMissing |
已有同意,但账户缺少真实值。 | 注册邮箱或补全缺失姓名。 |
规则拒绝、账户禁用等其他 403 都是终态,不会包含 Errand。
{ "reason": "ClaimConsentRequired", "claims": { "email": { "requirement": "REQUIRED", "state": "UNKNOWN" }, "firstName": { "requirement": "OPTIONAL", "state": "UNKNOWN" }, "lastName": { "requirement": "OFF", "state": "UNKNOWN" }, "staticAvatar": { "requirement": "SYNTHETIC_ONLY", "state": "UNKNOWN" }, "animatedAvatar": { "requirement": "OFF", "state": "UNKNOWN" } }, "errand": { "errandKey": "ernd_...", "url": "https://via.sudomimus.com/errand?key=ernd_...", "expiresAt": "2026-06-10T12:30:00Z" }}- 在用户的系统浏览器中打开
errand.url。 - 把
errandKey当作 bearer secret;轮询状态时也使用它。 - Errand 只能使用一次,并在 30 分钟后过期。
sequenceDiagram
autonumber
participant Client as 原生客户端
participant Native as Native API
participant Browser as 系统浏览器
participant Via as via
Client->>Native: POST /direct-issue/...
Native-->>Client: 403 { reason, claims, errand }
Client->>Browser: 打开 errand.url
Browser->>Via: 完成所需任务
Via-->>Browser: 已完成
loop 可选的状态轮询
Client->>Native: GET /errand/{errandKey}/status
Native-->>Client: PENDING 或 COMPLETED
end
Note over Client: 按凭据类型准备证明(新 Steam 票据或 PublicKey 断言)
Client->>Native: 携带该证明调用一次 POST /direct-issue/...
Native-->>Client: 当前签发检查通过则返回令牌,否则返回错误
准备重试证明
Section titled “准备重试证明”| 凭据 | 每次重新请求直接签发时使用的证明 |
|---|---|
| Steam | 通过 GetAuthTicketForWebApi 获取新票据。即使原请求返回的是 Errand,原票据也已受重放保护。 |
| AccessKey | 凭据仍然有效时,可以复用标识符和密钥。 |
| PublicKey | 使用新的 jti 和当前请求体的哈希重新签署断言。可以复用已注册的密钥,不能复用上次断言。 |
收到 COMPLETED、EXPIRED,或从刷新失败恢复时,都应遵守这些规则。Errand 完成只表示账户资料或同意步骤已完成;签发仍取决于当前凭据、应用和策略检查。等待期间应查询状态接口,不要反复提交凭据。
轮询不是强制的。客户端也可以让用户在浏览器完成后手动确认,再执行重试。
curl https://native-api.sudomimus.com/errand/ernd_.../status# → { "status": "PENDING" }# → { "status": "COMPLETED" }# → { "status": "EXPIRED" }建议大约每两秒轮询一次,并设置合理的总超时。EXPIRED 会刻意覆盖未知、格式错误、已消费和真正过期的 key;收到后按上述凭据规则准备证明,再重新请求直接签发以获取新交接。状态端点本身永远不会签发令牌。
只要现有 Errand 至少还剩 15 分钟,且待办事项没有变化,通过凭据验证的新请求通常会返回同一个 Errand,使用户进度继续关联到同一个 URL。
- 仅需同意:不要求额外登录,因为凭据持有者已经证明自己控制着一项可签发令牌的凭据。
- 需要写入身份数据:浏览器会要求登录,且登录账户必须与 Steam ticket、AccessKey 或 PublicKey 解析出的账户一致。
- Optional 与 Synthetic 声明永远不会创建 Errand。
- Session API
/refresh与 OIDC/token不会内嵌 Errand。原生会话在 refresh 时被阻塞,需要重新执行 direct-issue。