阅读主题
授权码兑换
POST /api/server/auth/exchange 由可信游戏服务器调用。H5、Android 与 iOS SDK 获得一次性授权码后,将 appId、code、codeVerifier 交给游戏服务器;游戏服务器校验应用允许列表,再添加服务端密钥转发平台。
请求
请求头为 Content-Type: application/json 与 X-API-Key: <当前逻辑游戏服务端密钥>。请求体严格限制以下三个字段,多余字段也返回校验错误。
| 字段 | 类型与约束 | 来源 |
|---|---|---|
appId | 字符串,去首尾空白后 1~100 字符 | 当前已批准应用 |
code | 字符串,20~100 字符 | 本次 SDK 登录授权码 |
codeVerifier | 字符串,43~128 字符,仅字母、数字、.、_、~、- | 与此次登录 challenge 配对的 PKCE 验证器 |
以下代码必须在游戏服务器执行,变量来自通过允许列表检查的 SDK 交换请求。不要将环境变量值输出给客户端。
js
const response = await fetch(`${process.env.ACCOUNT_ORIGIN}/api/server/auth/exchange`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.GAME_SERVER_KEY,
},
redirect: 'error',
signal: AbortSignal.timeout(15000),
body: JSON.stringify({ appId, code, codeVerifier }),
});
const body = await response.json();
if (!response.ok) {
// 将可处理的业务错误交给调用方;不要记录完整请求凭证。
throw new Error(body.error?.code ?? 'EXCHANGE_FAILED');
}
const session = body;完整可运行的本地代理入口与环境配置见 服务端起步。该示例仍需对新 App ID 修改允许列表;生产应建立自己的游戏会话与角色映射。
生命周期与响应
授权码有效期为 60 秒,绑定 App ID 和 PKCE challenge,只能消费一次。SDK 检查登录返回的 state 是否匹配,防止串入其他登录事务;兑换接口不接收 state,不要将它当第四个字段传入。
成功返回 HTTP 200,结构为 {token, user, role, expiresAt}。token 是不透明玩家会话,当前有效期 12 小时;它不是 JWT,客户端不能通过解码推断权限。数据库保存会话摘要。
user 包含 id、gameUserId、nickname、phone、avatar、restricted、createdAt。role 包含 id、name、server、level、diamonds、power,是本地模拟角色。当前响应中的手机号为完整字段,游戏服务端应按实际必要性使用与展示。
js
const identityResponse = await fetch(`${process.env.ACCOUNT_ORIGIN}/api/me`, {
headers: { Authorization: `Bearer ${session.token}` },
});
if (!identityResponse.ok) throw new Error('玩家会话校验失败');
const { user, role } = await identityResponse.json();
// 将 user.gameUserId 映射到本游戏账号;role 不等于已接入外部游戏存档。/api/me 只返回 {user, role},不会重新返回 token,也不会延长会话有效期。账号受限、应用停用、凭据相关撤销等会使会话无法继续使用,详见 身份与会话。
重试规则与排错
| 情况 | 处理 |
|---|---|
INVALID_GAME_KEY | 检查 App ID 与密钥是否属于同一逻辑游戏,是否已轮换;不要向玩家请求密钥 |
INVALID_AUTH_CODE | 授权码过期、已消费、App ID 不匹配或 verifier 不匹配;重新发起完整登录 |
APP_UNAVAILABLE | 在控制台检查审批、应用状态与开发者状态;等待恢复后再登录 |
VALIDATION_ERROR | 检查字段、长度和多余属性,不重复提交相同错误请求 |
| 网络超时或响应丢失 | 结果不确定;重新登录获取新授权码,不循环兑换旧码 |
RATE_LIMITED | 停止密集重试,退避后重新完成登录 |
当前兑换按请求 IP 限制每分钟 120 次,是单进程限流;反向代理与多实例场景需重新验证来源识别和限流设计。应用准入检查先于密钥校验,因此未批准应用可能先收到 403,而不是 401。
最小验收
一次正确兑换成功;同一码二次兑换失败;错误 verifier、错误游戏密钥及其他 App ID 兑换失败。正确 token 调用 /api/me 返回同一 gameUserId。新的一次登录应该使用全新 PKCE 与 state,不能复用旧事务值。
实现依据
packages/server/src/app.ts:exchangeSchema、exchange、player、GET /api/me。packages/sdk/src/index.ts:PKCE 与 state 生成、校验及 exchange 回调。packages/server/src/security.ts:随机 token 与摘要。examples/game-server.mjs:服务器代理、来源和 App ID 允许列表。