Repository navigation
Api Authentication
StackPanel 有四种认证通道:
| 通道 | 凭证 | 适用 |
|---|---|---|
| 服务端会话 |
Authorization: Bearer <token> 或 Cookie |
Web BFF 与后台 |
| 平台 API Token | Authorization: Bearer sp_... |
开放平台 /api/v1、/mcp
|
| OAuth 第三方登录 | 提供方授权码 | 用户登录 |
| 渠道签名 | Ed25519 请求签名头 |
/sp/v1/* 渠道伙伴 |
会话是不透明随机串(前缀 sess_),状态存于 Redis,多副本共享、可即时吊销。
请求头或 Cookie 均可携带:
curl -H "Authorization: Bearer <token>" http://127.0.0.1:3001/auth/me登录/注册由内置 login 插件提供(POST /login、POST /register):
POST /login
{ "email": "admin@stackpanel.local", "password": "NewPass123456" }
→ {
"token": "sess_...",
"user": { "id": "...", "email": "...", "role": "ADMIN", "status": "ACTIVE" }
}- 形态:
sess_+ 32 字节随机串,不是 JWT,服务端不解析载荷。 - 存储:Redis
sp:session:<token>(JSON:userId/createdAt/lastSeenAt), 另有sp:session:user:<userId>集合用于批量吊销。 - 有效期:
SESSION_TTL_SECONDS(默认 43200 = 12 小时),访问时滑动续期, 但最多每 60 秒写一次。 - 可吊销:登出、改密、封禁会立即在各副本生效(区别于旧的无状态 JWT)。
- 兼容:旧版 HS256 JWT 仍被接受,并在首次访问时平滑迁移为服务端会话
(
apps/api/src/plugins/auth.ts的extractSession)。
登录成功后,API 同时设置 HttpOnly Cookie(默认名 sp_session):
Set-Cookie: sp_session=<token>; HttpOnly; SameSite=Lax; Path=/; Max-Age=43200
Cookie 与 Bearer 使用同一会话 token。extractSession 先取 Bearer 头,其次取 Cookie
(token = bearer ?? cookie)。
BFF 模式下,Web 的
src/proxy.ts(Next 16 替代 middleware)只做/admin边缘鉴权; 真正从 Cookie 取 token 并在转发到 API 时放入 Bearer 头的是apps/web/src/lib/api.ts的getAuthedApiClient()(经getSessionToken())。
| 属性 | 值 | 说明 |
|---|---|---|
httpOnly |
true | 防止 JS 读取 |
sameSite |
lax | 防止 CSRF |
secure |
API_COOKIE_SECURE |
HTTPS 后应设为 true |
path |
/ | 全站有效 |
1. GET /auth/oauth/providers → 获取可用提供者
2. GET /auth/oauth/:id/authorize → 获取授权 URL(重定向用户)
3. POST /auth/oauth/:id/callback → 交换凭证,获取 token
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_ 前缀)→ 解析 ApiToken:
effectivePermissions = token.scopes ∩ 用户当前权限
3. 否则查服务端会话(Redis sp:session:<token>):
命中 → 查 DB user,检查 status === 'ACTIVE',注入 request.user
4. 否则若是旧 JWT(三段式)→ verifySession 后迁移为服务端会话
用户被禁用(status: DISABLED)后,已有会话在下次访问时立即失效(会话被丢弃)。
自服务端点(需登录会话):
| 方法 | 路径 | 说明 |
|---|---|---|
| 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_(STACKPANEL_API_TOKEN_PREFIX可改),库内只存哈希。 - scope 必须 ⊆ 持有者当前权限;
effectivePermissions是 token scope 与用户权限的交集, 因此用户降权会立即收窄其所有 Token。 - 支持 IP 白名单、过期时间;每 Token RPM(
OPEN_API_TOKEN_RPM,默认 240)与并发上限 (OPEN_API_TOKEN_CONCURRENCY,默认关闭)在共享状态中计数。 - 管理端另有
/admin/api-tokens、/admin/api-tokens/:id/usage用于审计。
/sp/v1/* 使用 Ed25519 请求签名,签名覆盖方法、路径、时间戳、nonce 与原始请求体;
nonce 单次有效,超出签名窗口或重放返回 401,每渠道 RPM 超限返回 429。
写操作(POST)另需 Idempotency-Key。完整协议见
StackPanel Partner API v1。
GET /auth/me
→ { "user": { "id": "...", "email": "...", "role": "ADMIN", "status": "ACTIVE", "lastLoginAt": "...", "createdAt": "...", "updatedAt": "..." } }轻量会话探针(永不 401,供 Web BFF / proxy 首层门禁使用):
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