技术文档

开放平台接入与协议细则

接入前由平台管理员登记应用类型、精确回调和最小 scope。先完成 OIDC 用户授权,再按实际需要启用签名 API 或通知。三种凭据的区别与数据边界见技术概览;本文依据 identity/server/protocols.ts、oidc.ts、open-api.ts 说明当前契约。

OIDC 授权次序

  1. 读取发行者 https://account.thestarline.cn/oidc 的发现文档,保存并验证 issuer,读取协议端点与 JWKS。
  2. 为每次登录生成新的 state、nonce 和 PKCE verifier,保存到发起浏览器或服务端受保护事务。使用 S256 challenge,授权请求带 response_type=code、client_id、已登记 redirect_uri 与 scope。
  3. 用户在 Account 完成认证和授权后,接入方回调先匹配本地 state,再用 code、原 redirect_uri 和 verifier 换票。机密客户端按发现配置使用 client_secret_basic;公开客户端不发送伪造密钥。
  4. 验证 ID Token 的 JWKS 签名、issuer、audience、nonce、过期时间,以 sub 建立本地账户关联。消费并清除本次事务,拒绝回调重放。

授权码只有 60 秒,交互为 300 秒。丢失 verifier、回调不一致或代码已使用时应重新开始登录,不能尝试另一回调或省略 PKCE。具体协议错误由 OIDC 实现返回,不套用普通 /api 的所有 JSON 错误形状。当前没有文档依据可承诺刷新令牌或无限离线授权。

管理接口与权限

下列接口均为受保护的管理操作,需平台管理员身份、允许的管理认证方式以及浏览器 CSRF;不能把示例做成接入方公开接口。

方法与路径 主要字段 返回或行为
POST /api/admin/applications name,type,redirectUris,scopes id,clientId,机密类型另含一次性返回的 clientSecret
POST /api/admin/applications/:id/status enabled 布尔值 更新状态,停用撤销授权门禁
POST /api/admin/applications/:id/api-credentials apiScopes 启用并返回 apiSecret,apiScopes
POST /api/admin/applications/:id/api-credentials/rotate JSON 对象 返回新 apiSecret,previousExpiresAt
POST /api/admin/applications/:id/api-credentials/disable JSON 对象 清除 API 密钥并禁用
POST /api/admin/applications/:id/webhook enabled,启用时 webhookUrl 配置通知,启用生成密钥
POST /api/admin/applications/:id/webhook/rotate JSON 对象 返回新 webhookSecret,立即生效
POST /api/admin/applications/:id/webhook/test JSON 对象 返回 delivered,不等于后续事件保证送达

API 与通知只对已启用的机密客户端开放。API scope 必须含 users.read,可加 users.email、users.phone;这些名称与 OIDC 的 email、phone 是两层权限。用户也可在 Account 撤销应用授权,管理员不应通过重新开 API 开关绕过用户的撤销。

应用内部 id 用于管理路由,clientId 用于协议和签名请求头,二者不能互换。列表只显示配置摘要,不提供取回原始密钥的通用接口。接入方遗失密钥时应按受控轮换流程更新,并核对所有调用实例是否已切换;不要从浏览器缓存、客户端安装包或普通日志寻找和分发旧密钥。创建、轮换响应只在受保护的服务端配置链路接收,避免将其写入业务审计正文。

HMAC 请求签名

请求头为 x-starline-key(clientId)、x-starline-timestamp(十进制 Unix 秒)、x-starline-nonce(16–128 个字符)和 x-starline-signature(64 位十六进制 HMAC-SHA256)。canonical 字符串按以下五行拼接,行间只有一个换行,正文后不额外补换行:

METHOD
原始路径与查询串
timestamp
nonce
原始正文

METHOD 使用大写;GET 正文为空。路径不含 scheme 或 host,但保留查询参数原顺序、百分号编码和原始 URL 表达。JSON 签名前后必须发送完全相同的 UTF-8 字节,空格或换行变化也会改变签名。以下为可移植的合成示例,不包含真实应用凭据:

import { createHmac, randomBytes } from 'node:crypto';
const path = '/api/open/v1/ping';
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = randomBytes(24).toString('base64url');
const body = '{}';
const canonical = ['POST', path, timestamp, nonce, body].join('\n');
const signature = createHmac('sha256', process.env.OPEN_API_SECRET)
  .update(canonical, 'utf8').digest('hex');
const response = await fetch(`https://account.thestarline.cn${path}`, {
  method: 'POST', body,
  headers: {
    'content-type': 'application/json',
    'x-starline-key': process.env.OPEN_CLIENT_ID,
    'x-starline-timestamp': timestamp,
    'x-starline-nonce': nonce,
    'x-starline-signature': signature
  }
});

ping 成功返回 {status:"ok",appId:clientId}。读取用户改用 GET /api/open/v1/users/:id,签名正文为空;id 是已完成该应用授权的 Account 用户主键。返回 {user:{id,banned,email?,phone?}}。不要用聊天用户 UUID 替换 Account ID,也不要把字段缺省当成空字符串覆盖已保存资料。

服务端允许时间偏差正负 300 秒;时间戳必须为安全整数。nonce 在每个应用范围内持久化去重,其保存期限与签名窗口相关。同一请求因网络超时需再次发送时,应生成新 nonce、时间戳和签名;这样只更新防重放证明,不意味着可以重复执行未来的非幂等业务接口。当前公开业务路由只含 ping 与读取用户。

Webhook 验证的特殊约定

通知正文为 {event,id,createdAt,appId,data},createdAt 为毫秒,签名头 timestamp 为秒。接收端必须保存原始正文后再验签,不能先 JSON parse、重排键后重新序列化。

当前 deliverSignedWebhook 调用签名函数时,方法固定 POST、canonical 路径固定为字面值 /。即使配置的接收 URL 是 https://receiver.example.invalid/events/account,验签第二行仍应使用 /,不是实际请求路径。这是当前源码的协议行为,升级时需与平台同步确认,不能套用普通开放 API 的路径规则。

验签后检查 appId、事件类型与业务目标,按事件 id 去重,再快速返回 2xx。接收端的时间窗、nonce 保存与幂等策略由自己实现,平台不会替接收端建立防重放存储。API 密钥旧值有 5 分钟轮换宽限;Webhook 没有该宽限,双方需要协调切换,或接收端在约定窗口内自行保留旧验签值。

失败定位与验证范围

结果 当前常见原因 首要检查
401 UNAUTHORIZED 头缺失、签名错误、时间过偏、nonce 重复、应用或 API 未启用 原始字节、时钟、密钥种类与开关
403 FORBIDDEN 缺少 users.read 等 scope API scope 与用户 OIDC 同意分别核对
404 NOT_FOUND 用户不存在或授权门禁、关联会话已无效 用户主键、会话和用户撤销状态
400 INVALID_INPUT 非法回调、通知 URL、应用参数,或开放 API 正文超过 16,384 字节 精确 URL、类型、数组范围与正文大小
限额拒绝 当前来源、目标或应用限额桶命中 降低频率、有界退避,不并发轰炸
delivered:false 网络、非 2xx、目标被阻止或超时 接收日志与审计结果,不能假定已自动重投

联调至少包含 PKCE 缺失、state/nonce 不匹配、授权码重放、同意范围缩减、会话撤销、签名正文变化、重复 nonce、时钟边界、API 轮换窗口、Webhook 特殊路径、停用与恢复。identity/tests/open-api.test.ts 等测试能支持契约复核;真实接入还需接入方回调、通知与密钥轮换的独立验收记录。

Markdown 原文完整文档文本