---
title: SDK
description: 官方 Sudomimus SDK —— 面向 Connect 与 Native API 的强类型客户端，提供 TypeScript、Python、C# 版本。
editUrl: true
head: []
template: doc
sidebar:
  order: 1
  hidden: false
  attrs: {}
pagefind: true
draft: false
---

import { CardGrid, LinkCard, Tabs, TabItem } from "@astrojs/starlight/components";

Sudomimus SDK 根据公开的 OpenAPI 3.1 契约生成。契约位于 [`sudomimus/sudomimus-spec`](https://github.com/sudomimus/sudomimus-spec)：[`connect.yaml`](https://github.com/sudomimus/sudomimus-spec/blob/main/connect.yaml) 描述 Connect 协议，[`native.yaml`](https://github.com/sudomimus/sudomimus-spec/blob/main/native.yaml) 描述 `native-api` direct-issue，[`device.yaml`](https://github.com/sudomimus/sudomimus-spec/blob/main/device.yaml) 描述 `device-api` device authorization。SDK 源码位于 [`sudomimus/sudomimus`](https://github.com/sudomimus/sudomimus)，TypeScript、Python 和 C# 版本位于同一个 monorepo。SDK 在 HTTPS API 之上提供序列化、请求和响应类型、JWT 解析、应用公钥缓存、签名验证辅助及结构化错误，无需在每个集成中重复实现。

如果你更想直接调用 API，可参阅 [Web 应用集成](/zh-cn/connect/flow/) 查看 curl、Node.js、Python、Go 的 HTTP 调用片段。

## 可用 SDK

所有包目前处于 **alpha** 阶段，可以使用，API 会跟随规范更新，但首个稳定版本发布前仍可能发生变化。

<CardGrid>
<LinkCard
    title="TypeScript / JavaScript"
    description="@sudomimus/connect、@sudomimus/native、@sudomimus/token —— 面向 Node.js 与浏览器。"
    href="https://github.com/sudomimus/sudomimus/tree/master/sdks/typescript/packages"
/>
<LinkCard
    title="Python"
    description="sudomimus-connect、sudomimus-native、sudomimus-token —— 同步（ConnectClient）与异步（AsyncConnectClient）双版本。"
    href="https://github.com/sudomimus/sudomimus/tree/master/sdks/python/packages"
/>
<LinkCard
    title="C#"
    description="Sudomimus.Native 与 Sudomimus.Token。C# 的 Connect 包尚未发布。"
    href="https://github.com/sudomimus/sudomimus/tree/master/sdks/csharp/src"
/>
</CardGrid>

每种语言的三个包按职责切分：

- **`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 参考](/zh-cn/api/device/) 供直接 HTTP 客户端使用。专用 SDK 包在 SDK 仓库引入后会补充到这里。

## 安装

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```bash
# pnpm
pnpm add @sudomimus/connect

# npm
npm install @sudomimus/connect

# yarn
yarn add @sudomimus/connect
```
</TabItem>
<TabItem label="Python">
```bash
# uv
uv add sudomimus-connect

# pip
pip install sudomimus-connect
```
</TabItem>
</Tabs>

## 初始化客户端

客户端需要知道如何用应用的 client-auth 私钥给 `/establish` 请求签名。在构造时一次性提供私钥，SDK 在每一次相关调用时自动签名。

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
import { ConnectClient } from "@sudomimus/connect";

const client = new ConnectClient({
    baseUrl: "https://connect-api.sudomimus.com",
    // clientAuth: 配置应用的 client-auth 私钥，
    //             以便 SDK 在 /establish 和 /revoke-all 上自动签名。
    //             精确的配置项见包 README。
});
```
</TabItem>
<TabItem label="Python">
```python
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`，方法名一致。
</TabItem>
</Tabs>

## Establish —— 开启一次会话

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
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;
```
</TabItem>
<TabItem label="Python">
```python
from sudomimus_connect import EstablishRequest

inquiry = client.establish(EstablishRequest(
    applicationAnchor="my-app",
))
# inquiry.exposureKey, inquiry.hiddenKey
```

Request 字段是 camelCase（`applicationAnchor`、`returnMethods`）—— 因为它们由 OpenAPI schema 生成。方法名是 snake_case。
</TabItem>
</Tabs>

## Status poll —— 查询会话状态

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
const status = await client.statusPoll({ exposureKey, hiddenKey });

if (status.status === "REALIZED") {
    const { confirmationKey } = status;
    // 进入 redeem 阶段
}
```

响应根据 `status` 字段判别（`"PENDING"` 或 `"REALIZED"`）；只有 `REALIZED` 才会带上 `confirmationKey`。
</TabItem>
<TabItem label="Python">
```python
from sudomimus_connect import StatusPollRequest

status = client.status_poll(StatusPollRequest(
    exposureKey=exposure_key,
    hiddenKey=hidden_key,
))
```
</TabItem>
</Tabs>

## Redeem —— 兑换令牌

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
const tokens = await client.redeem({
    exposureKey,
    hiddenKey,
    confirmationKey,
});

const { accessToken, refreshToken } = tokens;
```
</TabItem>
<TabItem label="Python">
```python
from sudomimus_connect import RedeemRequest

tokens = client.redeem(RedeemRequest(
    exposureKey=exposure_key,
    hiddenKey=hidden_key,
    confirmationKey=confirmation_key,
))
# tokens.accessToken, tokens.refreshToken
```
</TabItem>
</Tabs>

## Refresh / introspect / logout

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
// 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 });
```

端点语义见 [管理会话](/zh-cn/guides/managing-sessions/)。`/refresh` 会轮换 refresh token —— 它返回一个新的 `accessToken` **和**一个新的 `refreshToken`，而你提交上来的那个会被消费；请持久化替代者，并在下次使用它。`/refresh`、`/introspect`、`/logout` 自鉴权（令牌即凭据）；`/revokeAll` 需要构造时配置的 client-auth 凭据。
</TabItem>
<TabItem label="Python">
```python
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.revokedCount
```
</TabItem>
</Tabs>

## Info —— 查询应用元数据

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
const info = await client.info({
    applicationAnchor,
    locale: "zh-CN",
});

// info.applicationName, info.applicationPublicKey
```
</TabItem>
<TabItem label="Python">
```python
from sudomimus_connect import InfoRequest

info = client.info(InfoRequest(
    applicationAnchor="my-app",
    locale="zh-CN",
))
# info.applicationName, info.applicationPublicKey
```
</TabItem>
</Tabs>

## 验证 access token

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
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。完整说明见 [令牌与验证](/zh-cn/concepts/tokens-and-verification/)。
</TabItem>
<TabItem label="Python">
```python
access = client.verify_access_token(tokens.accessToken)
# access.body.subject
```

匹配的 refresh 验证方法是 `verify_refresh_token`。
</TabItem>
</Tabs>

## 处理 API 错误

<Tabs syncKey="sdk-lang">
<TabItem label="TypeScript">
```ts
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 状态码承载信息。
</TabItem>
<TabItem label="Python">
Python 会抛出包含相同字段的强类型异常。具体类名和属性请参考 [`sudomimus-connect` 包 README](https://github.com/sudomimus/sudomimus/tree/master/sdks/python/packages/sudomimus-connect)。
</TabItem>
</Tabs>

## C#

C# SDK 当前发布两个包，都处于 alpha：

- **`Sudomimus.Native`** —— `native-api` direct-issue 客户端（Steam ticket、AccessKey）。
- **`Sudomimus.Token`** —— 令牌解析工具，作用与 `@sudomimus/token` 相同。

C# 的 `Sudomimus.Connect` **尚未发布** —— 在 C# 中接 Connect 协议，请直接调 HTTPS 端点（请求形状见 [Web 应用集成](/zh-cn/connect/flow/)），并使用 `Sudomimus.Token` 验证拿到的令牌。源码位于 [`sdks/csharp/src`](https://github.com/sudomimus/sudomimus/tree/master/sdks/csharp/src)。

## 源码

<CardGrid>
<LinkCard
    title="GitHub 上的 SDK monorepo"
    description="所有官方 SDK 包的源码、issue 与发布标签都在这里。"
    href="https://github.com/sudomimus/sudomimus"
/>
<LinkCard
    title="OpenAPI 契约"
    description="sudomimus/sudomimus-spec —— SDK 据以生成的权威 connect.yaml 与 native.yaml。"
    href="https://github.com/sudomimus/sudomimus-spec"
/>
</CardGrid>