Repository navigation
Api Authentication
StackPanel 有四种认证通道:
| 通道 | 凭证 | 适用 |
|---|---|---|
| 服务端会话 |
Authorization: Bearer <token> 或 Cookie |
Web BFF 与后台 |
| 平台 API Token | Authorization: Bearer sp_... |
开放平台 /api/v1、/mcp
|
| OAuth 第三方登录 | 提供方授权码 | 用户登录 |
| 渠道签名 | Ed25519 请求签名头 |
/sp/v1/* 渠道伙伴 |
会话是不透明随机串:sess_ 前缀加 32 字节随机数的 base64url 编码。状态存于 Redis,
多副本共享、可即时吊销。请求头或 Cookie 均可携带:
curl -H "Authorization: Bearer <token>" http://127.0.0.1:3001/auth/me登录与注册由内置 login 插件提供:
POST /login
{ "email": "admin@stackpanel.local", "password": "NewPass123456" }
→ 200 {
"token": "sess_...",
"user": { "id": "...", "email": "...", "role": "ADMIN", "status": "ACTIVE" }
}POST /register
{ "email": "admin@stackpanel.local", "password": "NewPass123456" }
→ 201 { "token": "sess_...", "user": { ... } }注册要求密码至少 8 位。两个端点都会同时设置会话 Cookie。
- 形态:
sess_+ 32 字节随机数(base64url)。它不是 JWT,服务端不解析载荷。 - 存储:Redis
sp:session:<token>,值为 JSON(userId/createdAt/lastSeenAt); 另有sp:session:user:<userId>集合,用于按用户批量吊销。 - 有效期:
SESSION_TTL_SECONDS(默认 43200,即 12 小时)。访问时滑动续期,但两次 续期写入之间至少间隔 60 秒。 - 可吊销:登出、改密、封禁会立即在各副本生效。
- 兼容:旧版 HS256 JWT(三段式 token)仍被接受,并在首次带
reply访问时迁移为 服务端会话、重发 Cookie;无reply时按确定性 session id 采用,使重复请求复用 同一会话。相关实现为apps/api/src/plugins/auth.ts的extractSession。
登录成功后,API 同时设置 HttpOnly Cookie,默认名 sp_session(SESSION_COOKIE_NAME
可改):
Set-Cookie: sp_session=<token>; HttpOnly; SameSite=Lax; Path=/; Max-Age=43200
Cookie 与 Bearer 使用同一会话 token。extractSession 先取 Bearer 头,其次取 Cookie:
const token = bearer ?? cookie;| 属性 | 值 | 说明 |
|---|---|---|
httpOnly |
true | 防 JS 读取 |
sameSite |
lax | 防 CSRF |
secure |
API_COOKIE_SECURE |
HTTPS 部署应设为 true |
path |
/ | 全站有效 |
maxAge |
SESSION_TTL_SECONDS |
与会话有效期一致 |
1. GET /auth/oauth/providers → 获取可用提供者
2. GET /auth/oauth/:id/authorize → 获取授权 URL(重定向用户)
3. POST /auth/oauth/:id/callback → 交换凭证,获取 token
/authorize 会把 state 写入 Redis(键 oauth:state:<state>)并设置
sp_oauth_state_api Cookie;未传 state 时服务端生成一个并回显。
GET /auth/oauth/providers
→ { "providers": [{ "id": "wowid", "name": "WowID" }] }GET /auth/oauth/wowid/authorize?state=random_state&codeChallenge=xxx&nonce=xxx
→ { "authorizeUrl": "https://account.wowcraft.cn/oauth2/authorize?...", "state": "random_state" }POST /auth/oauth/wowid/callback
{
"code": "authorization_code",
"state": "random_state",
"codeVerifier": "PKCE_verifier",
"nonce": "nonce_value"
}
→ { "token": "...", "user": { ... } }- 以
providerId + externalId查UserIdentity。 - 已存在则匹配本地用户。
- 不存在则检查 email 冲突;无冲突时创建用户(无密码)并写入身份绑定。
POST /logout
→ 204(吊销会话并清除 Cookie)apps/api/src/plugins/auth.ts 的 extractSession:
请求 → extractSession:
1. 取 Bearer 头,其次 Cookie
2. 若像平台 API Token(默认 sp_ 前缀,兼容历史 sk-sp- 前缀)→ 解析 ApiToken:
effectivePermissions = token.scopes ∩ 用户当前权限
3. 否则查服务端会话(Redis sp:session:<token>):
命中 → 查 DB user,检查 status === 'ACTIVE',注入 request.user
4. 否则若是旧 JWT(三段式)→ verifySession 后迁移为服务端会话
用户被禁用(status: DISABLED)后,已有会话在下次请求时即失效。被拒绝的 token 会
进入一个短 TTL 的拒绝缓存,避免对同一无效凭证反复查库。
自服务端点需要登录会话:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /me/api-tokens |
列出本人 Token(不返回明文) |
| GET | /me/api-tokens/scopes |
可授予的 scope 列表 |
| POST | /me/api-tokens |
创建;明文仅返回一次 |
| PATCH | /me/api-tokens/:id |
改名 / 改 scope / 启停 / 改过期 |
| DELETE | /me/api-tokens/:id |
吊销 |
| GET | /me/api-tokens/:id/usage |
本人该 Token 的调用审计 |
- 明文格式为
sp_前缀加 24 字节随机数的 base64url 编码;前缀可用STACKPANEL_API_TOKEN_PREFIX覆盖,历史前缀sk-sp-继续被接受。库内只存sha256(明文)与打码前缀。 - scope 必须 ⊆ 持有者当前权限。
effectivePermissions是 token scope 与用户当前权限 的交集,因此用户降权会立即收窄其所有 Token。 - 支持 IP 白名单与过期时间。每 Token RPM(
OPEN_API_TOKEN_RPM,默认 240)与并发 上限(OPEN_API_TOKEN_CONCURRENCY,默认 0 表示关闭)在共享状态中计数。 -
lastUsedAt回写有节流,最多每 5 分钟落库一次。 - 管理端另有
/admin/api-tokens、/admin/api-tokens/:id/usage用于审计。
/sp/v1/* 使用 Ed25519 请求签名。签名覆盖方法、路径、时间戳、nonce 与原始请求体;
nonce 单次有效,超出签名窗口或重放返回 401,每渠道 RPM 超限返回 429。写操作
(POST)另需 Idempotency-Key。实现位于 apps/api/src/reseller/。
GET /auth/me
→ { "user": { "id": "...", "email": "...", "role": "ADMIN", "status": "ACTIVE", "lastLoginAt": "...", "createdAt": "...", "updatedAt": "..." } }轻量会话探针,供 Web BFF 与其 proxy 作为 /admin 的首层门禁,未登录时返回
{ "user": null } 而非 401:
GET /auth/session
→ { "user": { "id": "...", "role": "ADMIN" } } # 未登录时 { "user": null }GET /auth/identities
→ { "identities": [{ "id": "...", "providerId": "wowid", "email": "...", "displayName": "...", "createdAt": "..." }] }开始
- 核心开发
构建内核扩展
- 插件
- 主题与前端包
AI 与自动化
- 内核 Agent
- MCP
对接平台
- Open Platform API
- Partner API