Base Account / Integration
账号系统接入文档
按能力模块拆分的接入规范,供产品工程和 AI Agent 按需获取。
Machine Readable Spec
AI Agent 可直接读取下方 JSON 进行自动接入。结构版本字段为 docVersion。 包含认证系统和邀请码系统的完整规范。
展开查看 Machine Readable JSON
{
"docVersion": "2026-03-06.3",
"generatedAt": "2026-03-06.3",
"service": "base-account-auth",
"issuer": "https://user.stringzhao.life",
"audience": "base-account-client",
"jwksUrl": "https://user.stringzhao.life/.well-known/jwks.json",
"authorizeContract": {
"entryPath": "/authorize",
"requiredQuery": [
"return_to",
"state"
],
"optionalQuery": [
"service (deprecated)"
],
"callbackQuery": [
"authorized",
"state"
]
},
"endpoints": [
{
"method": "GET",
"path": "/authorize",
"auth": "none",
"purpose": "统一授权入口。后端基于 return_to 的 origin 识别服务,未登录跳转登录页,已登录则按 consent 状态决定是否直接回跳。可选参数 prompt=select_account:即使已授权,也强制显示账号选择界面(用于多账号切换场景)。",
"errors": [
"400 invalid_authorize_request",
"400 invalid_return_to",
"400 invalid_service",
"400 invalid_state"
]
},
{
"method": "POST",
"path": "/api/auth/send-code",
"auth": "none",
"purpose": "发送邮箱验证码,返回 requestId。",
"errors": [
"400 invalid_input",
"429 rate_limited",
"502 email_delivery_failed"
]
},
{
"method": "POST",
"path": "/api/auth/verify-code",
"auth": "none",
"purpose": "校验验证码并签发 access/refresh token(同时写入 cookie)。",
"errors": [
"400 invalid_code",
"429 too_many_attempts",
"403 account_disabled"
]
},
{
"method": "POST",
"path": "/api/auth/authorize/approve",
"auth": "access_token",
"purpose": "用户在首次授权页点击同意后写入 consent 记录并返回回跳地址。",
"errors": [
"401 missing_access_token",
"400 invalid_input",
"400 invalid_return_to",
"400 invalid_service"
]
},
{
"method": "POST",
"path": "/api/auth/refresh",
"auth": "refresh_token",
"purpose": "刷新 access token 和 refresh token。",
"errors": [
"401 invalid_refresh_token",
"400 missing_refresh_token"
]
},
{
"method": "POST",
"path": "/api/auth/logout",
"auth": "refresh_token",
"purpose": "注销当前会话并清理 cookie。",
"errors": [
"200 幂等,refresh token 失效也可安全调用"
]
},
{
"method": "GET",
"path": "/api/auth/me",
"auth": "access_token",
"purpose": "获取当前 access token 对应用户信息。",
"errors": [
"401 missing_access_token",
"401 invalid_access_token"
]
},
{
"method": "GET",
"path": "/.well-known/jwks.json",
"auth": "none",
"purpose": "下游服务用于验证 JWT 的公钥集合。",
"errors": [
"必须公网可访问,且与 AUTH_ISSUER 保持同源策略"
]
}
],
"integrationSteps": [
{
"title": "准备环境变量",
"detail": "下游服务至少配置 AUTH_ISSUER、AUTH_AUDIENCE、AUTH_JWKS_URL;账号中心配置 AUTH_ALLOWED_RETURN_ORIGINS、AUTH_ALLOWED_RETURN_SUFFIXES。"
},
{
"title": "接入统一授权入口",
"detail": "外部服务统一跳转 /authorize?return_to&state(service 可传但会被忽略),禁止直接跳 /login。"
},
{
"title": "登记服务域名",
"detail": "推荐通过 CLI 注册服务:ba admin services create --origin https://your-app.example.com(也可在 Admin Console 的 Services 区域手动登记)。需先登记/启用 origin,再让外部服务发起授权。"
},
{
"title": "处理回跳并建立应用会话",
"detail": "回跳后在服务端读取共享 access_token cookie 验签 JWT,提取用户 email,然后创建应用自有的 gateway session cookie(HMAC 签名的 email + 过期时间)。重要:不要直接依赖共享 access_token cookie 作为日常登录态,否则跨应用切换账号会导致身份污染。"
},
{
"title": "按需接入 JWT 校验",
"detail": "如果你的服务需要在后端验签 access token,再接入 auth-sdk 的 JWKS 验签。"
}
],
"templates": [
{
"id": "node-middleware",
"title": "Node / Express 鉴权中间件",
"runtime": "Node 20+ / Express"
},
{
"id": "next-route-handler",
"title": "Next.js Route Handler 保护接口",
"runtime": "Next.js App Router"
},
{
"id": "frontend-login-flow",
"title": "前端最小登录流程",
"runtime": "Browser / SPA"
},
{
"id": "frontend-authorize-entry",
"title": "外部服务统一授权入口",
"runtime": "Browser / Web App"
},
{
"id": "frontend-authorize-callback",
"title": "回跳校验 + 获取用户态(简易版)",
"runtime": "Browser / Web App"
},
{
"id": "next-gateway-session",
"title": "Next.js Gateway Session 模式(推荐)",
"runtime": "Next.js App Router"
},
{
"id": "account-switching",
"title": "账号切换(多账号场景)",
"runtime": "Browser / Web App"
}
],
"checklist": [
"AUTH_ISSUER 与账号服务域名保持一致(当前: https://user.stringzhao.life)。",
"AUTH_AUDIENCE 在账号服务和下游服务严格一致(当前: base-account-client)。",
"AUTH_JWKS_URL 配置为 https://user.stringzhao.life/.well-known/jwks.json。",
"新接入服务需要先登记并启用 origin。推荐使用 CLI:ba admin services create --origin <url>(也可在 Admin Console -> Services 手动操作)。",
"/authorize 的 service 参数已弃用(兼容保留,但后端不再依赖该参数判定服务)。",
"AUTH_ALLOWED_RETURN_ORIGINS 建议至少包含 http://localhost:3000, https://user.stringzhao.life, https://stringzhao.life。",
"AUTH_ALLOWED_RETURN_SUFFIXES 建议配置为 .stringzhao.life,.vercel.app(一次覆盖你全部 Vercel 服务)。",
"外部服务统一从 /authorize 进入登录授权流程,不直接拼接 /login。",
"业务接口对 401/403/429 做显式处理,不把鉴权失败当系统异常。",
"access_token / refresh_token cookie 在 .stringzhao.life 域共享,任一子域的登录/切换会覆盖所有子域的登录态。接入方应创建应用自有的 gateway session cookie(参考模板),避免跨应用账号污染。",
"上线后至少做一次 send-code / verify-code / me 全链路回归。"
],
"externalIntegrationChecklist": [
"授权入口统一改为 /authorize?return_to=<absolute_url>&state=<opaque_state>。",
"service 参数可传可不传(兼容保留),但不能再用于服务身份判定。",
"发起授权前生成并持久化 state(建议 randomUUID + sessionStorage)。",
"回跳后必须校验 authorized=1 且 returned state 与本地 state 完全一致。",
"每个业务回跳域名(return_to origin)需先开通并启用。推荐使用 CLI:ba admin services create --origin <url>(也可在 /admin -> Services 手动操作)。",
"回跳后在服务端读取共享 access_token cookie 并验签 JWT(避免前端 CORS),然后创建应用自有的 gateway session cookie 作为日常登录态。不建议直接依赖共享 access_token cookie(跨应用账号污染风险)。",
"后端 JWT 验签配置保持一致:AUTH_ISSUER、AUTH_AUDIENCE、AUTH_JWKS_URL。",
"业务侧显式处理 400 invalid_service / 400 invalid_return_to / 401 invalid_access_token。",
"上线前至少完成首次授权、重复授权直跳、停用服务拦截、icon 展示回退四项回归。",
"如需账号切换功能,跳转 /authorize 时附加 prompt=select_account 参数。已授权用户将看到账号选择界面,可选择当前账号、历史登录账号或登录新账号。"
],
"invitationCodes": {
"description": "邀请码系统:每用户每应用可生成 N 个一次性邀请码,兑换后记录邀请关系。",
"defaultQuota": 3,
"endpoints": [
{
"method": "POST",
"path": "/api/auth/invitation-codes/generate",
"auth": "access_token",
"purpose": "为当前用户在指定应用下生成一个一次性邀请码。每用户每应用有配额限制(普通用户默认 3,管理员 1000)。serviceKey 需先通过 CLI 注册:ba admin services create --origin <url>。",
"errors": [
"401 missing_access_token",
"400 invalid_input",
"400 invalid_service",
"403 invitation_quota_exceeded"
]
},
{
"method": "POST",
"path": "/api/auth/invitation-codes/redeem",
"auth": "access_token",
"purpose": "兑换邀请码。仅需传入 code,无需 serviceKey(系统自动从邀请码记录中读取)。一次性使用,兑换后记录邀请关系(谁邀请了谁)。普通用户不能兑换自己生成的邀请码;管理员账号可用于自助开通。服务代理模式:使用 API key 认证时可传入 userId 代表终端用户兑换,适用于下游服务有独立 session 的场景。",
"errors": [
"401 missing_access_token",
"400 invalid_invitation_code",
"400 self_redeem_not_allowed",
"409 invitation_code_already_redeemed"
]
},
{
"method": "POST",
"path": "/api/auth/invitation-codes/validate",
"auth": "access_token",
"purpose": "仅校验邀请码有效性,不消费。仅需传入 code,无需 serviceKey。适用于前端实时校验场景。",
"errors": [
"401 missing_access_token",
"400 invalid_input"
]
},
{
"method": "GET",
"path": "/api/auth/invitation-codes?serviceKey=my-app",
"auth": "access_token",
"purpose": "列出当前用户在指定应用下生成的所有邀请码及配额信息。",
"errors": [
"401 missing_access_token",
"400 invalid_input"
]
},
{
"method": "POST",
"path": "/api/auth/invitation-codes/revoke",
"auth": "access_token",
"purpose": "撤销自己生成的 ACTIVE 状态邀请码。已兑换的码不可撤销。",
"errors": [
"401 missing_access_token",
"404 invitation_code_not_found",
"403 forbidden",
"400 invalid_invitation_code"
]
}
],
"integrationSteps": [
{
"title": "生成邀请码",
"detail": "已登录用户调用 POST /api/auth/invitation-codes/generate,传入 serviceKey,获得 8 位邀请码。每用户每应用默认可生成 3 个(管理员配额为 1000)。serviceKey 需先通过 CLI 注册服务:ba admin services create --origin https://your-app.example.com。"
},
{
"title": "分享邀请码",
"detail": "将邀请码通过任意渠道(聊天、邮件、社交媒体等)发送给受邀人。"
},
{
"title": "受邀人兑换",
"detail": "受邀人登录后调用 POST /api/auth/invitation-codes/redeem,仅需传入 code(不需要 serviceKey)。系统自动从邀请码记录中读取 serviceKey,记录邀请关系并返回 serviceKey 和邀请者 ID。"
},
{
"title": "下游业务处理",
"detail": "下游服务根据 redeem 返回的 serviceKey + creatorId 决定后续动作(如:解锁功能、发放奖励、建立推荐关系等)。"
}
]
},
"feedback": {
"description": "用户反馈系统:Bug/建议/咨询提交 + 双向工单对话,按 serviceKey 隔离来源项目。",
"types": [
"BUG",
"SUGGESTION",
"INQUIRY"
],
"statuses": [
"PENDING",
"IN_PROGRESS",
"RESOLVED",
"IGNORED"
],
"userStatusMapping": {
"IGNORED": "CLOSED"
},
"commentVisibility": [
"INTERNAL",
"PUBLIC"
],
"commentAuthorRole": [
"ADMIN",
"USER"
],
"attachmentLimit": {
"count": 4,
"sizeBytes": 2097152,
"mime": "image/*"
},
"rateLimit": {
"windowSec": 60,
"maxPerUser": 3
},
"endpoints": [
{
"method": "POST",
"path": "/api/feedback",
"auth": "access_token",
"purpose": "提交反馈(Bug/建议/咨询),可选附带已上传的截图 url。",
"errors": [
"invalid_input(400): 描述为空 / 类型非枚举 / 附件 url 非 https",
"too_many_attachments(400): 附件数 > 4",
"invalid_service_key(400): serviceKey 未注册",
"unauthorized(401): 未登录",
"rate_limited(429): 单用户 60s 内提交 > 3 条"
]
},
{
"method": "GET",
"path": "/api/feedback/mine",
"auth": "access_token",
"purpose": "查看我的反馈列表(含对外消息 messages,仅 PUBLIC;不含内部备注)。",
"errors": [
"unauthorized(401): 未登录"
]
},
{
"method": "POST",
"path": "/api/feedback/attachments",
"auth": "access_token",
"purpose": "上传单张截图(multipart file),返回 blob url。校验 image/* + ≤2MB。",
"errors": [
"invalid_file_type(400): 非 image/*",
"file_too_large(413): > 2MB",
"unauthorized(401)"
]
},
{
"method": "POST",
"path": "/api/feedback/:id/messages",
"auth": "access_token",
"purpose": "用户在反馈下追加消息(双向对话,通知管理员邮箱)。",
"errors": [
"invalid_input(400): 内容为空",
"forbidden(403): 反馈不属于该用户(横向越权)",
"feedback_not_found(404)"
]
},
{
"method": "GET",
"path": "/api/admin/feedback",
"auth": "admin",
"purpose": "管理员看板列表:按 serviceKey/type/status 筛选 + 描述搜索 + 游标分页。",
"errors": [
"forbidden(403): 非管理员"
]
},
{
"method": "GET",
"path": "/api/admin/feedback/:id",
"auth": "admin",
"purpose": "管理员详情:反馈全文 + 附件 + 全部 comments(含 INTERNAL 备注 + PUBLIC 消息 + authorRole/visibility)。",
"errors": [
"feedback_not_found(404)",
"forbidden(403): 非管理员"
]
},
{
"method": "PATCH",
"path": "/api/admin/feedback/:id/status",
"auth": "admin",
"purpose": "状态流转(PENDING/IN_PROGRESS/RESOLVED/IGNORED)。状态变更不再自动发邮件。",
"errors": [
"invalid_status(400): 非法状态值",
"feedback_not_found(404)",
"forbidden(403)"
]
},
{
"method": "POST",
"path": "/api/admin/feedback/:id/comments",
"auth": "admin",
"purpose": "添加内部备注(visibility=INTERNAL,仅管理员可见,用户看不到)。",
"errors": [
"invalid_input(400): 内容为空",
"feedback_not_found(404)",
"forbidden(403)"
]
},
{
"method": "POST",
"path": "/api/admin/feedback/:id/reply",
"auth": "admin",
"purpose": "回复用户(visibility=PUBLIC,用户可见 + 触发回访邮件,正文含回复内容)。",
"errors": [
"invalid_input(400): 内容为空",
"feedback_not_found(404)",
"forbidden(403)"
]
}
]
}
}