SDK
Sudomimus SDK 根据公开的 OpenAPI 3.1 契约生成。契约位于 sudomimus/sudomimus-spec:connect.yaml 描述 Connect 协议,native.yaml 描述 native-api direct-issue,device.yaml 描述 device-api device authorization。SDK 源码位于 sudomimus/sudomimus,TypeScript、Python 和 C# 版本位于同一个 monorepo。SDK 在 HTTPS API 之上提供序列化、请求和响应类型、JWT 解析、应用公钥缓存、签名验证辅助及结构化错误,无需在每个集成中重复实现。
如果你更想直接调用 API,可参阅 Web 应用集成 查看 curl、Node.js、Python、Go 的 HTTP 调用片段。
可用 SDK
Section titled “可用 SDK”所有包目前处于 alpha 阶段,可以使用,API 会跟随规范更新,但首个稳定版本发布前仍可能发生变化。
每种语言的三个包按职责切分:
connect—— 完整的 Connect API 客户端(Establish、StatusPoll、Redeem、Refresh、Info、Introspect、Logout、RevokeAll)。用在应用后端。native—— Native API 客户端(Steam ticket direct-issue、AccessKey direct-issue)。用在桌面应用、游戏、CLI。token—— 仅令牌解析工具(verifyAccessToken、verifyRefreshToken、kty常量)。适合只需验证令牌、不调 API 的服务。
connect 与 native 包内部已经依赖 token,大多数集成不必显式安装它。
Device API 已经有公开的 OpenAPI 参考 供直接 HTTP 客户端使用。专用 SDK 包在 SDK 仓库引入后会补充到这里。
# pnpmpnpm add @sudomimus/connect
# npmnpm install @sudomimus/connect
# yarnyarn add @sudomimus/connect# uvuv add sudomimus-connect
# pippip install sudomimus-connect初始化客户端
Section titled “初始化客户端”客户端需要知道如何用应用的 client-auth 私钥给 /establish 请求签名。在构造时一次性提供私钥,SDK 在每一次相关调用时自动签名。
import { ConnectClient } from "@sudomimus/connect";
const client = new ConnectClient({ baseUrl: "https://connect-api.sudomimus.com", // clientAuth: 配置应用的 client-auth 私钥, // 以便 SDK 在 /establish 和 /revoke-all 上自动签名。 // 精确的配置项见包 README。});from sudomimus_connect import ( ConnectClient, ConnectClientAuthWithKey,)
with ConnectClient( client_auth=ConnectClientAuthWithKey( application_anchor="my-app", private_key_pem=open("client-auth-private.pem").read(), ),) as client: ...也提供异步版本 AsyncConnectClient,方法名一致。
Establish —— 开启一次会话
Section titled “Establish —— 开启一次会话”const inquiry = await client.establish({ applicationAnchor: process.env.SUDOMIMUS_APPLICATION_ANCHOR!, returnMethods: [ { type: "CALLBACK", payload: { callbackUrl: "https://your-app.com/auth/callback" }, }, ],});
const { exposureKey, hiddenKey } = inquiry;from sudomimus_connect import EstablishRequest
inquiry = client.establish(EstablishRequest( applicationAnchor="my-app",))# inquiry.exposureKey, inquiry.hiddenKeyRequest 字段是 camelCase(applicationAnchor、returnMethods)—— 因为它们由 OpenAPI schema 生成。方法名是 snake_case。
Status poll —— 查询会话状态
Section titled “Status poll —— 查询会话状态”const status = await client.statusPoll({ exposureKey, hiddenKey });
if (status.status === "REALIZED") { const { confirmationKey } = status; // 进入 redeem 阶段}响应根据 status 字段判别("PENDING" 或 "REALIZED");只有 REALIZED 才会带上 confirmationKey。
from sudomimus_connect import StatusPollRequest
status = client.status_poll(StatusPollRequest( exposureKey=exposure_key, hiddenKey=hidden_key,))Redeem —— 兑换令牌
Section titled “Redeem —— 兑换令牌”const tokens = await client.redeem({ exposureKey, hiddenKey, confirmationKey,});
const { accessToken, refreshToken } = tokens;from sudomimus_connect import RedeemRequest
tokens = client.redeem(RedeemRequest( exposureKey=exposure_key, hiddenKey=hidden_key, confirmationKey=confirmation_key,))# tokens.accessToken, tokens.refreshTokenRefresh / introspect / logout
Section titled “Refresh / introspect / logout”// refresh 会轮换令牌 —— 捕获新的那个并持久化,供下次使用。const { accessToken, refreshToken: newRefreshToken } = await client.refresh({ refreshToken });await store.saveRefreshToken(newRefreshToken);
const { status, recommendedRecheckSeconds } = await client.introspect({ accessToken });// status: "active" | "revoked" | "expired" | "not_found"
await client.logout({ refreshToken });
const { revokedCount } = await client.revokeAll({ subject });端点语义见 管理会话。/refresh 会轮换 refresh token —— 它返回一个新的 accessToken 和一个新的 refreshToken,而你提交上来的那个会被消费;请持久化替代者,并在下次使用它。/refresh、/introspect、/logout 自鉴权(令牌即凭据);/revokeAll 需要构造时配置的 client-auth 凭据。
from sudomimus_connect import ( RefreshRequest, IntrospectRequest, LogoutRequest, RevokeAllRequest,)
fresh = client.refresh(RefreshRequest(refreshToken=tokens.refreshToken))state = client.introspect(IntrospectRequest(accessToken=tokens.accessToken))client.logout(LogoutRequest(refreshToken=tokens.refreshToken))revoked = client.revoke_all(RevokeAllRequest(subject="sub_9SQ5535CRWNDDM2T"))# revoked.revokedCountInfo —— 查询应用元数据
Section titled “Info —— 查询应用元数据”const info = await client.info({ applicationAnchor, locale: "zh-CN",});
// info.applicationName, info.applicationPublicKeyfrom sudomimus_connect import InfoRequest
info = client.info(InfoRequest( applicationAnchor="my-app", locale="zh-CN",))# info.applicationName, info.applicationPublicKey验证 access token
Section titled “验证 access token”import { TokenError } from "@sudomimus/token";
try { const token = await client.verifyAccessToken(rawJwt); // token.body.subject, token.body.firstName, token.body.lastName?} catch (error) { if (error instanceof TokenError) { // 结构化的令牌验证失败 } throw error;}verifyAccessToken 会解析 JWT、检查 kty === "Access"、验证过期时间,然后调用 /info 取回 applicationPublicKey(按 applicationAnchor 缓存)并验签。Refresh token 走对应的 verifyRefreshToken(kty === "Refresh")。密钥轮换后可用 clearPublicKeyCache() 强制重新拉取。
Connect access token 的验证是 per-application 的,不走共享 JWKS。完整说明见 令牌与验证。
access = client.verify_access_token(tokens.accessToken)匹配的 refresh 验证方法是 verify_refresh_token。
处理 API 错误
Section titled “处理 API 错误”import { ConnectApiError } from "@sudomimus/connect";
try { await client.redeem({ exposureKey, hiddenKey, confirmationKey });} catch (error) { if (error instanceof ConnectApiError) { // error.status —— HTTP 状态码 // error.reason —— 稳定的机器可读 reason code(若存在) // error.body —— 原始 `{ reason?: string }` payload(若存在) } throw error;}当底层失败符号被标记为 PRIVATE 时 reason 字段会被省略 —— 此时只有 HTTP 状态码承载信息。
Python 会抛出包含相同字段的强类型异常。具体类名和属性请参考 sudomimus-connect 包 README。
C# SDK 当前发布两个包,都处于 alpha:
Sudomimus.Native——native-apidirect-issue 客户端(Steam ticket、AccessKey)。Sudomimus.Token—— 令牌解析工具,作用与@sudomimus/token相同。
C# 的 Sudomimus.Connect 尚未发布 —— 在 C# 中接 Connect 协议,请直接调 HTTPS 端点(请求形状见 Web 应用集成),并使用 Sudomimus.Token 验证拿到的令牌。源码位于 sdks/csharp/src。