设备码轮询与错误
设备码授权客户端的大部分时间都在等待。好的实现会把 /device-token 当作一个状态机:pending 继续轮询,slow_down 放慢速度,终止错误停止流程,成功则消费这次会话。
POST /device-authorize 之后,保存 deviceCode,展示 userCode,然后用不快于返回 interval 的频率开始轮询。
let intervalSeconds = authorize.interval;
while (true) { await sleep(intervalSeconds * 1000);
const res = await fetch("https://device-api.sudomimus.com/device-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ deviceCode: authorize.deviceCode }), });
const body = await res.json();
if (res.ok) { return body; // { accessToken, refreshToken, claims, ... } }
if (body.error === "authorization_pending") continue; if (body.error === "slow_down") { intervalSeconds = body.interval ?? intervalSeconds + 5; continue; }
throw new Error(body.error);}如果客户端轮询太快,服务端可能返回 slow_down。发生这种情况后,后续轮询使用响应里的 interval。
POST /device-token 对轮询状态使用 RFC 8628 device-flow 错误词汇。客户端应该按 error 分支处理;不要期待这里返回 Sudomimus { "reason": "..." } wire reason。
| Error | 含义 | 客户端行为 |
|---|---|---|
authorization_pending | 用户还没有批准或拒绝。 | 按当前间隔继续轮询。 |
slow_down | 客户端轮询太快。 | 增加间隔;如果响应带 interval,使用它。 |
access_denied | 用户拒绝、批准失败,或策略已不再允许签发。 | 停止轮询,展示拒绝/失败状态。 |
expired_token | 设备授权会话已过期。 | 停止轮询;重新从 /device-authorize 开始。 |
invalid_request | deviceCode 格式错误、未知,或已经被消费。 | 停止轮询;必要时重新开始。 |
server_error | 批准后签发 token 失败。 | 停止轮询,报告可重试的服务故障。 |
成功响应使用 Sudomimus 普通 camelCase JSON 形状。轮询失败刻意使用 device-flow 的 error 词汇,这样公共客户端可以按熟悉的设备码状态机实现。
过期与一次性
Section titled “过期与一次性”每个设备授权会话都很短暂。生产默认值目前是 expiresIn: 600 秒,但客户端应该使用 /device-authorize 返回的值,而不是硬编码生命周期。
一旦 /device-token 成功,这次会话就被消费。用同一个 deviceCode 重复调用 /device-token 会返回 invalid_request,不能再签发第二组令牌。
Code 处理
Section titled “Code 处理”两个 code 的处理方式不同:
| 值 | 谁能看到 | 用途 |
|---|---|---|
deviceCode | 只有发起客户端 | /device-token 的高熵 bearer secret |
userCode | 用户和浏览器批准页面 | 用户比较并确认的短码 |
不要展示 deviceCode,不要写入日志,不要放进浏览器 URL,也不要发给你的应用后端,除非那个后端就是负责轮询 /device-token 的组件。deviceCode 在过期前是 bearer secret;谁拿到它,谁就能轮询并消费已批准的会话。
userCode 可以展示,但它不是 token,也不能独自签发 token。它的作用是让浏览器里的用户确认:自己批准的正是终端、启动器或设备界面上显示的那次客户端会话。
没有 client secret
Section titled “没有 client secret”设备码授权是为公共客户端准备的。/device-authorize 不需要 client-auth JWT,因为这样的客户端无法保护签名密钥。Sudomimus 改由应用配置来把关:
applicationAnchor必须解析到一个启用的应用。- 应用必须有一条启用的 Layer 3
DEVICE_CODEReturnRule。 - 浏览器批准仍然走应用的 Layer 1 认证规则。
- realize 出来的账户仍然要通过 Layer 2,批准才能完成。
如果你有能保护 client-auth 私钥的机密后端,Connect 或 Connect 浏览器轮询 可能更合适。
Claims 与令牌操作
Section titled “Claims 与令牌操作”/device-token 返回的 access token 和 refresh token 与其他 Sudomimus 流程相同。Claim 分享同样遵循应用的 claim policy 和用户的 grant state,和 Connect 或 Native direct-issue 一致。
Device API 没有 refresh 端点。初始交换完成后,使用 Connect:
connect-api POST /refreshconnect-api POST /logoutconnect-api POST /introspectconnect-api POST /revoke-all
令牌生命周期调用见管理会话;精确请求/响应 schema 见 Device API 参考。