跳转到内容

原生流程

查看 Markdown

Connect 浏览器流程假设你能把浏览器跳转到 via.sudomimus.com,并接收回调。对 Web 应用来说没问题,但对桌面应用、游戏和无头工具来说就尴尬了。

根据客户端的能力,原生客户端有三种流程可选。其中两种是 Native APInative-api.sudomimus.com)的一次性 direct-issue 端点——Steam direct-issue 和 AccessKey direct-issue。第三种「浏览器轮询」则是一条 Connect API 流程(/establish/status-poll/redeem),由原生客户端自行驱动,并不运行在 Native API 上。

如果你的客户端是公共客户端,不应该拿到应用 client-auth 私钥,也不应该预置 AccessKey secret,请改用设备码授权。设备码授权才是面向 CLI、启动器等公共客户端的验证码确认流程。

流程适用场景往返次数端点
浏览器轮询任何能拉起系统浏览器的原生客户端3+ (establish + N×poll + redeem)connect POST /establish/status-poll/redeem
Steam direct-issue通过 Steam 发行、能调用 Steamworks SDK 的游戏1native-api POST /direct-issue/steam-ticket
AccessKey direct-issue预先拿到应用级凭据、绑定到已存在账户的 CLI / 无头服务 / 启动器1native-api POST /direct-issue/access-key

当原生客户端能拉起用户的系统浏览器、但又不方便接收回调 URL 时,使用轮询流程:

  1. 客户端后端调用 connect POST /establish(带上应用的 client-auth JWT),声明 STATUS_POLL 返回方法,拿到 { exposureKey, hiddenKey }
  2. 客户端拉起系统浏览器并打开 https://via.sudomimus.com/?exposure-key=<exposureKey>
  3. 用户在浏览器中完成通行密钥或邮箱验证码挑战。
  4. 客户端每隔几秒调用一次 connect POST /status-poll,提交 { exposureKey, hiddenKey }。一旦用户完成,轮询会返回 { status: "REALIZED", confirmationKey }
  5. 客户端将三个密钥提交给 connect POST /redeem,换取 { accessToken, refreshToken }

这一方案适用于任何带默认浏览器的平台——Windows、macOS、Linux 桌面应用、Electron 等等。应用的 Layer 3 规则中必须允许 STATUS_POLL

/establish 就是标准的、用 client-auth 签名的 Connect 请求 —— 完整形状见 Web 应用 —— 只是返回方法换成 STATUS_POLL

Terminal window
curl -X POST https://connect-api.sudomimus.com/establish \
-H "Content-Type: application/json" \
-H "Authorization: SudomimusClientJWT $SUDOMIMUS_CLIENT_AUTH_JWT" \
-d '{
"applicationAnchor": "your-application",
"returnMethods": [ { "type": "STATUS_POLL", "payload": {} } ]
}'
# → { "exposureKey": "exp_...", "hiddenKey": "hid_..." }

随后用这两把 key 每隔几秒轮询一次 /status-poll。轮询带 client-auth JWT —— 授权它的是对 hiddenKey 的持有:

Terminal window
curl -X POST https://connect-api.sudomimus.com/status-poll \
-H "Content-Type: application/json" \
-d '{
"exposureKey": "exp_...",
"hiddenKey": "hid_..."
}'
# 用户仍在浏览器里鉴权时:
# { "status": "PENDING" }
# 用户完成后:
# { "status": "REALIZED", "confirmationKey": "cnf_..." }

轮询返回 REALIZED 后,将三个密钥提交给 connect POST /redeem,换取访问令牌和刷新令牌。该调用与 Web 流程中的 /redeem 相同。

对于通过 Steam 发行的游戏,Sudomimus 支持一种完全不打开浏览器的静默登录。用户看不到任何登录提示;他们的 Steam 身份会直接换成一个 Sudomimus 会话。

Terminal window
curl -X POST https://native-api.sudomimus.com/direct-issue/steam-ticket \
-H "Content-Type: application/json" \
-d '{
"applicationAnchor": "my-game",
"steamTicketHex": "<来自 Steamworks GetAuthTicketForWebApi 的 hex 编码 ticket>",
"steamAppId": 480
}'

流程:

  1. 游戏调用 Steamworks 的 ISteamUser::GetAuthTicketForWebApi("sudomimus")——不要GetAuthSessionTicket,两者是不同类型的 ticket,不能互换。identity 字符串必须是 "sudomimus"(大小写敏感);其他值会被拒绝。
  2. 游戏等待 GetTicketForWebApiResponse_t 回调,再使用该 ticket。
  3. 把 ticket 字节流 hex 编码后作为 steamTicketHex,连同 applicationAnchorsteamAppId 一起 POST 到 /direct-issue/steam-ticket
  4. Sudomimus 向 Steam 校验 ticket,查找或创建账户,然后 —— 在顺利路径上 —— 在同一次请求中返回 { accessToken, refreshToken }。如果应用要求 Steam 账户尚未提供的同意或资料,这一步会改为返回一个带 Errand 交接的 403 —— 见当 direct-issue 需要同意或资料时
  5. 拿到令牌后,游戏调用 Steamworks.CancelAuthTicket(handle) 收尾。

整个过程用户都不会离开游戏。Steam 账户就是身份来源。

应用必须配置:

  • Layer 1:一条 STEAM_TICKET AuthenticationRule,allowedSteamAppIds: number[] 中包含该游戏的 Steam App ID。
  • Layer 2:至少一条能命中的规则——通常是 STEAM_IDallowedSteamIds: ["*"] 表示放行任何已验证的 Steam 账户,或者给出一组具体的 SteamID64 字符串。
  • Layer 3:一条 DIRECT_ISSUE ReturnRule。

从未绑定过邮箱的「Steam-first」账户必须依赖 STEAM_ID(或 ACCOUNT_ALIAS / SECTOR_SUBJECT)类型的 Layer 2 规则;只有 EMAIL 规则会把它拒掉。

该端点需要 client-auth JWT——Steam ticket 本身就证明了用户身份以及这个二进制有权与该应用对话。

对应的 Web 流程。 浏览器中的「Sign in with Steam」按钮使用 Layer 1 的 STEAM_OPENID,而不是 STEAM_TICKET。两条路径都会解析为同一个用户级 Steam 身份,因此用户先从游戏登录后,也可以继续使用网页按钮登录,反之亦然,无需合并账户。STEAM_OPENID 的规则结构见认证规则

适用于没有 Steam ticket、但目标 Sudomimus 账户已经明确的场景——CLI 工具、自定义启动器、无头服务、自动化测试。「证明」是由 Sudomimus 签发的一对凭据(accessKeyIdentifier + accessKeySecret),在开发者门户中生成后通过线下方式交付给运维方。

Terminal window
curl -X POST https://native-api.sudomimus.com/direct-issue/access-key \
-H "Content-Type: application/json" \
-d '{
"applicationAnchor": "my-cli-tool",
"accessKeyIdentifier": "acs_k_<uuidv4>",
"accessKeySecret": "acs_t_<64 位小写 hex>"
}'

两个凭据字符串都带有强制前缀:

  • acs_k_ —— 公开 identifier,后接 UUIDv4。
  • acs_t_ —— 密文部分,后接 64 位小写 hex(在创建时只展示一次,之后无法恢复)。

前缀是规范形式的一部分。它们让两个部分在视觉上可区分,也方便 secret scanner 通过字面子串匹配到误提交的凭据。

应用必须配置:

  • Layer 1:一条 ACCESS_KEY_DIRECT AuthenticationRule(空 payload)。不显式开启则默认拒绝。
  • Layer 2:一条能匹配目标账户的规则——EMAILSTEAM_IDACCOUNT_ALIASSECTOR_SUBJECT
  • Layer 3:一条 DIRECT_ISSUE ReturnRule。

AccessKey 凭据无法创建新账户。 凭据绑定在一个已存在的 Sudomimus 账户上;该账户被删除后,所有与之绑定的凭据在登录时都会被拒绝。

凭据通过 with.sudomimus.com 中应用详情页的 Access keys Tab 管理。吊销是软删除(revokedAt 时间戳);轮换 = 吊销 + 重新签发。过期凭据不会自动清理,但在 handler 处会被拒绝。

该端点也不需要 client-auth JWT——access-key 的密文本身就是凭据。把 client-auth 私钥嵌进发行给运维的 CLI 里,任何人都能反编译出来,因此再叠一层并不带来真正的防御。

两个 direct-issue 端点都是一次性的读取者——它们没法弹出同意界面,也没法让用户敲入一封邮箱。所以当应用要求一条用户尚未授权的声明(claim),或要求账户还没有的数据(例如纯 Steam 账户没有邮箱)时,这次调用不能就这么成功。它会改为返回一个带 Errand403 —— 一段短暂的浏览器侧行,用户在那里完成这些工作:

{
"reason": "ClaimConsentRequired",
"claims": {
"email": { "requirement": "REQUIRED", "state": "UNKNOWN" },
"firstName": { "requirement": "OPTIONAL", "state": "UNKNOWN" },
"lastName": { "requirement": "OFF", "state": "UNKNOWN" }
},
"errand": {
"errandKey": "ernd_...",
"url": "https://via.sudomimus.com/errand?key=ernd_...",
"expiresAt": "2026-06-10T12:30:00Z"
}
}

reasonClaimConsentRequired(用户必须同意共享某条 required 声明)或 RequiredClaimDataMissing(同意已就位,但账户数据缺失)二者之一。Steam 与 AccessKey direct-issue 在这里行为完全一致。补救方式:

  1. 在用户的系统浏览器中打开 errand.url。页面会引导用户完成所有待办的登录、数据录入和同意。
  2. 每隔约 2 秒轮询 GET /errand/{errandKey}/statusnative-api),直到它报告 COMPLETED —— 或者干脆让用户在你的 UI 里告诉你他完成了。
  3. 同一个 direct-issue 调用重试一次。它现在会成功返回 { accessToken, refreshToken }
Terminal window
curl https://native-api.sudomimus.com/errand/ernd_.../status
# → { "status": "PENDING" } 用户仍在浏览器里操作
# → { "status": "COMPLETED" } 完成 —— 重试 direct-issue
# → { "status": "EXPIRED" } 已过期/已消费/未知 —— 重新跑 direct-issue 拿一个新的 errand

任一端点的 200 也会带一个 claims 块(形状与 403 里的相同),所以即便成功,你也能看出哪些可选声明被共享了、哪些被保留了。完整契约 —— 30 分钟存活期、为什么重试会复用同一个 errandKey、以及两种安全层级(仅同意 vs. 需要登录)—— 见 Errand

你的客户端是…
Web 应用Connect + via.sudomimus.com(常规流程)
能拉起浏览器的桌面应用 / Electron / 移动端Connect + 通过系统浏览器打开 via.sudomimus.com + connect /status-poll
没有 client secret 的公共 CLI / 启动器设备码授权
通过 Steam 发行的游戏native-api POST /direct-issue/steam-ticket(静默,一次往返)
通过预签发凭据绑定到已知账户的 CLI / 无头服务native-api POST /direct-issue/access-key(一次往返,使用 AccessKey)

无论走哪条路径,最终返回的令牌格式都是一样的——你的应用只需要处理同一种 access token 结构,与用户的认证方式无关。