@seaverse/auth-sdk
v0.3.8
Published
SDK for SeaVerse Backend API
Downloads
2,805
Readme
@seaverse/auth-sdk
SeaVerse Backend API 客户端 SDK - 提供完整的认证、容器管理、技能市场等功能
功能特性
- 🔐 用户认证 - 注册、登录、登出、密码重置
- 🌐 OAuth登录 - Google、Discord、GitHub 第三方登录
- 🎨 登录UI组件 - 开箱即用的精美登录弹窗
- ✨ Toast 通知 - 现代化玻璃态提示,CSS 自动注入
- 🌍 多环境支持 - Production、Staging、Development、Local
- ⚡ TypeScript - 完整的类型定义
- 🔧 认证集成 - 内置Auth、Hooks系统支持
- 🔄 响应格式兼容 - 自动兼容多种API响应格式
安装
npm install @seaverse/auth-sdk
# 或
pnpm add @seaverse/auth-sdk核心概念
App ID 和多租户架构
重要: 从 v0.2.0 开始,appId 是初始化 SDK 时的必需参数。
什么是 App ID?
每个应用都有唯一的 app_id:
app_id = "your app id"
多租户隔离
- 用户数据按
app_id隔离 - 同一邮箱可在不同应用中注册,使用不同密码
- 每个应用拥有独立的用户池
X-App-ID 请求头
SDK 会自动在每个请求的请求头中添加 X-App-ID,无需手动设置:
const client = new SeaVerseBackendAPIClient({
appId: 'game-abc123', // SDK 自动将此值添加到所有请求的 X-App-ID header
});
// 所有 API 调用都会自动携带 X-App-ID: game-abc123
await client.login({ email, password });
await client.getCurrentUser();快速开始
1. 基本使用
import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';
// 方式1: SeaVerse 平台应用(自动检测环境)
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:应用ID
});
// 方式2: 第三方应用(指定环境)
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:您的应用ID
environment: 'production', // 'production' | 'staging' | 'development' | 'local'
});
// 方式3: 自定义URL
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:您的应用ID
baseURL: 'https://custom-api.example.com',
});
// 方式4: 启用请求重试(默认禁用)
const client = new SeaVerseBackendAPIClient({
appId: 'your app id',
retryOptions: {
maxRetries: 3, // 最多重试 3 次
retryDelay: 1000, // 初始延迟 1000ms,每次重试延迟加倍
retryStatusCodes: [408, 429, 500, 502, 503, 504], // 这些状态码会触发重试
},
});
// 健康检查
const health = await client.getHealth();
console.log('Health:', health);2. 用户认证
// 注册新用户
const registerResult = await client.register({
email: '[email protected]',
password: 'SecurePassword123',
username: 'johndoe', // 可选,未提供则从邮箱自动生成
invitation_code: 'INVITE123', // 可选
frontend_url: 'https://mygame.com/verify', // 可选,邮箱验证链接的前端URL,默认为 window.location.href
});
// 检查注册结果
if (registerResult.success) {
console.log('注册成功:', registerResult);
} else if (registerResult.code === 'ACCOUNT_EXISTS') {
console.log('账户已存在,请直接登录');
console.log('错误详情:', registerResult.details);
}
// 登录
const loginResult = await client.login({
email: '[email protected]',
password: 'SecurePassword123',
});
// 保存token
localStorage.setItem('token', loginResult.token);
// ⚠️ 注意:邀请码自动跳转
// 如果服务器返回 INVITE_CODE_REQUIRED 错误且包含 redirectUrl,
// SDK 会自动将用户重定向到该 URL(通常是邀请码输入页面)
// 无需额外处理,页面会自动跳转
// 获取当前用户信息
const user = await client.getCurrentUser();
console.log('User:', user);
console.log('App ID:', user.app_id); // 多租户应用ID
console.log('Email verified:', user.email_verified);
// 登出
await client.logout();
// 忘记密码
await client.forgotPassword({
email: '[email protected]',
frontend_url: 'https://mygame.com/', // 可选,默认为 window.location.href
});
// 重置密码
await client.resetPassword({
token: 'reset-token-from-email',
new_password: 'NewSecurePassword123',
});3. OAuth 第三方登录 (Backend Proxy Mode)
SDK 使用 Backend Proxy Mode,Client Secret 永不暴露给前端,安全性更高。
优势:
- ✅ Client Secret 从不暴露给前端
- ✅ 支持任意开发者域名(无需在 OAuth 平台配置)
- ✅ 内置 CSRF 防护
- ✅ 零配置,开箱即用
工作流程:
- 前端调用
{provider}Authorize()获取 OAuth URL - 前端重定向用户到 OAuth 提供商
- 用户授权后,OAuth 提供商回调到固定的 account-hub URL
- account-hub 处理 OAuth,创建 JWT token
- account-hub 302 重定向到
return_url?token=xxx - 前端从 URL 提取 token 并存储
使用示例
方式1:使用默认 return_url(当前页面)
// Google 登录
const { authorize_url } = await client.googleAuthorize();
window.location.href = authorize_url;
// Discord 登录
const { authorize_url } = await client.discordAuthorize();
window.location.href = authorize_url;
// GitHub 登录
const { authorize_url } = await client.githubAuthorize();
window.location.href = authorize_url;方式2:自定义 return_url
// 登录后跳转到 dashboard
const { authorize_url } = await client.googleAuthorize({
return_url: 'https://mygame.com/dashboard'
});
window.location.href = authorize_url;在回调页面提取 token:
// URL: https://mygame.com/?token=eyJhbGc...
const token = new URLSearchParams(window.location.search).get('token');
if (token) {
localStorage.setItem('token', token);
// 登录成功,跳转或更新 UI
}OAuth 账号解绑
// 解绑 Google 账号
await client.unlinkGoogle();
// 解绑 Discord 账号
await client.unlinkDiscord();
// 解绑 GitHub 账号
await client.unlinkGithub();4. 使用登录UI组件
SDK 提供精美的登录弹窗组件,支持深色和浅色两种主题:
主题对比
| 主题 | 设计风格 | 适用场景 | |------|---------|---------| | Dark 🌙 | 动态网格渐变 + 玻璃态效果 | 科技感产品、游戏平台、深色界面应用 | | Light ☀️ | 提升的卡片设计 + 柔和阴影 | 商务应用、内容平台、浅色界面应用 |
基础用法
import { SeaVerseBackendAPIClient, AuthModal } from '@seaverse/auth-sdk';
import '@seaverse/auth-sdk/auth-modal.css'; // 导入样式
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:您的应用ID
environment: 'production',
});
// 创建登录弹窗
const authModal = new AuthModal({
client,
theme: 'dark', // 'dark' | 'light' - 默认为 'dark'
// 登录成功回调
onLoginSuccess: (token, user) => {
localStorage.setItem('token', token);
console.log('登录成功:', user);
},
// 注册成功回调
onSignupSuccess: (token, user) => {
localStorage.setItem('token', token);
console.log('注册成功:', user);
},
// 邀请码需求回调(可选)
onInviteCodeRequired: (userId, email) => {
console.log('需要邀请码激活账户:', userId, email);
// AuthModal 会自动显示邀请码输入界面
},
// 申请邀请码成功回调(可选)
onApplyInviteSuccess: (applicationId, email) => {
console.log('邀请码申请已提交:', applicationId, email);
// 可以在这里添加自定义逻辑,如显示提示信息
},
// 错误回调
onError: (error) => {
console.error('认证错误:', error.message);
},
// OAuth 配置(可选)
returnUrl: 'https://mygame.com/', // OAuth 登录后返回的 URL,可选,默认为 window.location.href
enableOAuth: {
google: true, // 启用 Google 登录
discord: true, // 启用 Discord 登录
github: true, // 启用 GitHub 登录
},
});
// 显示登录界面
authModal.show('login');
// 显示注册界面
authModal.show('signup');
// 隐藏弹窗
authModal.hide();
// 销毁弹窗
authModal.destroy();✨ 新特性:自动 Toast 通知
AuthModal 内置了现代化的 Toast 通知系统,无需额外配置:
// Toast 会自动显示,CSS 自动注入
// - 注册成功 → 绿色成功提示
// - 账号已存在 → 黄色警告提示
// - 登录失败 → 红色错误提示
// - 重置密码 → 蓝色信息提示
// 你也可以单独使用 Toast(CSS 自动注入)
import { Toast } from '@seaverse/auth-sdk';
Toast.success('操作成功', '数据已保存');
Toast.error('操作失败', '请稍后重试');
Toast.warning('注意', '账号已存在');
Toast.info('提示', '邮件已发送');重置密码流程
AuthModal 支持完整的密码重置流程:
- 用户触发忘记密码:在登录界面点击 "Forgot Password?" 链接
- 发送重置邮件:输入邮箱后,系统发送带有
reset_token的重置链接 - 自动唤起重置弹窗:用户点击邮件中的链接返回网站时,AuthModal 会自动检测 URL 中的
reset_token参数并显示重置密码表单 - 设置新密码:用户输入并确认新密码后提交
- 自动清理 URL:重置成功后自动清除 URL 中的
reset_token参数
整个流程无需额外代码,AuthModal 会自动处理:
// 1. 初始化 AuthModal(只需一次)
const authModal = new AuthModal({
client,
// ... 其他配置
});
// 2. 用户点击邮件中的重置链接后,AuthModal 会自动:
// - 检测 URL 中的 ?reset_token=xxx 参数
// - 显示重置密码表单
// - 用户提交新密码
// - 调用 client.resetPassword() API
// - 清理 URL 中的 reset_token 参数
// - 显示成功消息OAuth 配置说明
enableOAuth 参数是完全可选的:
- 如果不提供
enableOAuth,则不会显示任何第三方登录按钮 - 如果部分配置(如只启用 Google),则只显示已启用的按钮
- 如果完整配置所有平台,则显示所有第三方登录按钮
配置字段说明:
returnUrl:可选 - OAuth 登录后返回的 URL,不填则默认为window.location.hrefenableOAuth.google:是否启用 Google 登录enableOAuth.discord:是否启用 Discord 登录enableOAuth.github:是否启用 GitHub 登录
// 示例1:无OAuth按钮
const authModal1 = new AuthModal({
client,
theme: 'dark',
// 不传 enableOAuth,不显示任何OAuth按钮
});
// 示例2:只显示Google登录
const authModal2 = new AuthModal({
client,
theme: 'light',
returnUrl: 'https://mygame.com/dashboard',
enableOAuth: {
google: true,
// Discord 和 GitHub 未启用,不会显示这些按钮
},
});
// 示例3:显示所有OAuth按钮
const authModal3 = new AuthModal({
client,
theme: 'dark',
enableOAuth: {
google: true,
discord: true,
github: true,
},
});处理 OAuth 回调
在 Backend Proxy Mode 下,OAuth 登录后会重定向到 returnUrl?token=xxx。在页面加载时检查并处理token:
// 在页面加载时自动处理 OAuth 回调
const result = AuthModal.handleOAuthCallback({
client,
onLoginSuccess: (token) => {
localStorage.setItem('token', token);
console.log('OAuth 登录成功');
// 现在可以直接调用需要认证的接口
// handleOAuthCallback 已自动调用 client.setToken()
client.getCurrentUser()
.then(user => console.log('用户信息:', user));
},
});
if (result) {
console.log('处理了 OAuth 回调,token:', result.token);
}重要说明:
handleOAuthCallback()会自动调用client.setToken(token)来更新 client 的认证配置- 这意味着在
onLoginSuccess回调之后,所有需要认证的 API(如getCurrentUser()、logout()等)都会自动带上Authorizationheader
手动设置 Token
如果你不使用 AuthModal.handleOAuthCallback(),也可以手动设置 token:
// 从 URL 提取 token
const token = new URLSearchParams(window.location.search).get('token');
if (token) {
// 手动设置 token
client.setToken(token);
localStorage.setItem('token', token);
// 现在可以调用需要认证的接口
const user = await client.getCurrentUser();
}5. 容器管理
// 列出所有容器
const containers = await client.listContainers();
// 注册新容器
const result = await client.registerContainer({
containerId: 'container-123',
metadata: {
version: '1.0.0',
capabilities: ['skill-execution'],
},
});
// 获取容器信息
const container = await client.getContainer({
containerId: 'container-123',
});
// 容器心跳
await client.containerHeartbeat({
containerId: 'container-123',
status: 'healthy',
});
// 注销容器
await client.unregisterContainer({
containerId: 'container-123',
});
// 获取容器统计
const stats = await client.getContainerStats();6. 技能市场
// 列出市场技能
const skills = await client.listMarketplaceSkills({
category: 'productivity',
page: 1,
pageSize: 20,
});
// 获取技能详情
const skill = await client.getMarketplaceSkill({
skillId: 'skill-123',
});
// 安装技能
await client.installSkill({
skillId: 'skill-123',
});
// 列出已安装的技能
const userSkills = await client.listUserSkills();
// 卸载技能
await client.uninstallSkill({
skillId: 'skill-123',
});7. 邀请码管理
// 申请邀请码(当用户没有邀请码时)
const application = await client.applyInvite({
email: '[email protected]',
reason: 'I want to join this amazing platform to build innovative applications and connect with the community.'
});
if (application.success) {
console.log('申请已提交:', application.data);
console.log('申请ID:', application.data.id);
console.log('状态:', application.data.status); // 'pending'
} else {
// 处理错误
if (application.code === 'APPLICATION_DUPLICATE') {
console.log('您在过去24小时内已提交过申请');
} else {
console.error('申请失败:', application.error);
}
}
// 列出我的邀请码
const invites = await client.listInvites({
status: 'active',
page: 1,
page_size: 20,
});
console.log('我的邀请码:', invites.data.invites);
// 获取邀请码统计
const stats = await client.getInviteStats();
console.log('总邀请码数:', stats.data.total_codes);
console.log('活跃邀请码:', stats.data.active_codes);
console.log('总使用次数:', stats.data.total_uses);
// 获取邀请码详情
const invite = await client.getInvite('inv_abc123');
console.log('邀请码:', invite.data.code);
console.log('已使用:', invite.data.used_count);
console.log('最大使用次数:', invite.data.max_uses);
// 获取邀请码使用记录
const usages = await client.getInviteUsages('inv_abc123', {
page: 1,
page_size: 20,
});
console.log('使用记录:', usages.data.usages);8. 邮箱验证与邀请码绑定
邮箱验证(自动登录)
用户注册后会收到验证邮件,邮件中包含验证链接,格式为:frontend_url?verify_token=xxx
方式一:使用 AuthModal 自动处理(推荐)
import { AuthModal } from '@seaverse/auth-sdk';
// 创建 AuthModal 实例
const modal = new AuthModal({
client,
onLoginSuccess: (token, user) => {
localStorage.setItem('token', token);
console.log('邮箱验证并登录成功:', user);
},
onError: (error) => {
console.error('邮箱验证失败:', error);
}
});
// AuthModal 会自动检测 URL 中的 verify_token 参数
// 检测到后会自动:
// 1. 调用 verifyEmail() API 验证邮箱
// 2. 获取返回的 JWT token 并自动登录
// 3. 触发 onLoginSuccess 回调
// 4. 清理 URL 中的 verify_token 参数
// 5. 显示成功消息调试提示:
如果邮箱验证出现问题,请检查浏览器控制台日志:
// 正常情况应该看到:
[AuthModal] Detected verify_token, starting email verification...
[AuthModal] Email verification successful: { id: "xxx", email: "[email protected]", ... }
// 如果验证失败,会看到:
[AuthModal] Email verification failed: Error: ...方式二:手动处理邮箱验证
// 验证邮箱(从邮件链接中获取 verify_token)
const urlParams = new URLSearchParams(window.location.search);
const verifyToken = urlParams.get('verify_token');
if (verifyToken) {
const result = await client.verifyEmail(verifyToken);
// 自动登录:保存返回的 token
localStorage.setItem('token', result.data.token);
localStorage.setItem('refreshToken', result.data.refreshToken);
console.log('邮箱验证成功,已自动登录:', result.data.user);
// 重定向到主页
window.location.href = '/';
}邀请码绑定(账户激活)
当使用外部邮箱注册或 OAuth 登录但未提供邀请码时,会创建临时账户并需要后续绑定邀请码激活。
申请邀请码功能:
如果用户没有邀请码,AuthModal 提供了内置的申请功能:
- 在邀请码输入界面,用户可以点击 "Don't have an invitation code? Apply for one" 链接
- 自动跳转到申请邀请码表单
- 用户填写邮箱和申请原因(10-500字符)
- 提交后会调用
applyInvite()API,后台审核通过后会通过邮件发送邀请码
自动跳转机制:
- 当
login()返回INVITE_CODE_REQUIRED错误且包含redirectUrl时,SDK 会自动将页面重定向到该 URL - redirectUrl 通常包含
error_code=INVITE_CODE_REQUIRED&user_id=xxx&email=xxx参数 - 无需手动处理跳转逻辑,SDK 会自动完成
对于其他场景(如 OAuth 登录后的重定向),后端会直接重定向到 frontend_url?error_code=INVITE_CODE_REQUIRED&user_id=xxx&email=xxx。
方式一:使用 AuthModal 自动处理(推荐)
import { AuthModal } from '@seaverse/auth-sdk';
// 创建 AuthModal 实例
const modal = new AuthModal({
client,
onLoginSuccess: (token, user) => {
localStorage.setItem('token', token);
console.log('登录成功:', user);
},
onInviteCodeRequired: (userId, email) => {
// 可选:当需要邀请码时的自定义处理
console.log('需要邀请码激活账户:', userId, email);
}
});
// AuthModal 会自动检测 URL 中的以下参数组合:
// - error_code=INVITE_CODE_REQUIRED
// - user_id 或 temp_user_id(用户ID)
// - email(可选,用户邮箱)
//
// 检测到后会自动:
// 1. 显示邀请码输入界面
// 2. 用户输入邀请码后调用 bindInviteCode() API
// 3. 激活成功后自动登录(保存 token)
// 4. 清理 URL 中的参数调试提示:
如果邀请码弹窗没有出现,请检查浏览器控制台是否有以下日志:
// 正常情况应该看到:
[AuthModal] Detected INVITE_CODE_REQUIRED: {
errorCode: "INVITE_CODE_REQUIRED",
userId: "xxx",
tempUserId: null,
email: "[email protected]",
fullURL: "http://localhost:8001/?error_code=INVITE_CODE_REQUIRED&user_id=xxx&email=xxx"
}
// 如果 user_id 缺失,会看到错误提示:
[AuthModal] Missing user_id in URL parameters.
// 并弹出 alert 显示完整 URL,方便排查后端重定向问题方式二:手动处理邀请码绑定
// 1. 检查 URL 中是否需要邀请码
const urlParams = new URLSearchParams(window.location.search);
const errorCode = urlParams.get('error_code');
const userId = urlParams.get('user_id');
if (errorCode === 'INVITE_CODE_REQUIRED' && userId) {
// 2. 显示邀请码输入界面(自定义 UI)
const inviteCode = await showInviteCodeInput(); // 你的自定义UI
// 3. 绑定邀请码
const result = await client.bindInviteCode({
user_id: userId,
invite_code: inviteCode
});
// 4. 激活成功,自动登录
localStorage.setItem('token', result.data.token);
localStorage.setItem('refreshToken', result.data.refreshToken);
console.log('账户激活成功:', result.data.user);
// 5. 重定向到主页
window.location.href = '/';
}方式三:使用静态方法处理
import { AuthModal } from '@seaverse/auth-sdk';
// AuthModal 提供静态方法处理邀请码场景
const inviteCodeInfo = AuthModal.handleInviteCodeRequired(
{ client },
(userId, email) => {
// 自定义处理逻辑
const code = prompt(`请输入邀请码激活账户 (${email}):`);
if (code) {
client.bindInviteCode({ user_id: userId, invite_code: code })
.then(res => {
localStorage.setItem('token', res.data.token);
window.location.reload();
});
}
}
);9. 其他功能
// 获取API Service Token
const apiToken = await client.getApiServiceToken();
// 获取对话状态
const status = await client.getConversationStatus({
conversationId: 'conv-123',
});
// 获取语音Token
const speechToken = await client.getSpeechToken();高级配置
自定义认证
import { AuthFactory } from '@seaverse/auth-sdk';
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:应用ID
environment: 'production',
// 使用JWT认证
auth: AuthFactory.create({
type: 'jwt',
credentials: {
type: 'jwt',
token: localStorage.getItem('token'),
},
}),
});自定义Hooks
import { BuiltInHooks } from '@seaverse/auth-sdk';
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:应用ID
environment: 'production',
hooks: {
hooks: [
// 日志Hook
BuiltInHooks.createLoggerHook({
logLevel: 'debug',
logRequestBody: true,
logResponseBody: true,
}),
// 请求ID Hook
BuiltInHooks.createRequestIdHook(),
// 自定义Hook
{
type: 'beforeRequest',
name: 'custom-hook',
priority: 100,
handler: async (context) => {
console.log('Before request:', context.config.url);
},
},
],
},
});请求重试配置
SDK 支持自定义 HTTP 请求重试策略。默认情况下重试功能是禁用的(maxRetries: 0),以确保请求的可预测性。
默认行为(禁用重试)
const client = new SeaVerseBackendAPIClient({
appId: 'your app id',
// retryOptions 未设置,默认不重试
});
// 如果请求失败,会立即返回错误,不会重试启用基础重试
import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';
const client = new SeaVerseBackendAPIClient({
appId: 'your app id',
retryOptions: {
maxRetries: 3, // 最多重试 3 次
retryDelay: 1000, // 初始延迟 1000ms (1秒)
// 默认会对以下状态码重试:[408, 429, 500, 502, 503, 504]
},
});重试策略说明:
- 采用指数退避算法:第1次等待1秒,第2次等待2秒,第3次等待4秒
- 自动重试的 HTTP 状态码:
408- Request Timeout(请求超时)429- Too Many Requests(限流)500- Internal Server Error(服务器内部错误)502- Bad Gateway(网关错误)503- Service Unavailable(服务不可用)504- Gateway Timeout(网关超时)
- 网络错误(无响应)也会触发重试
自定义重试状态码
const client = new SeaVerseBackendAPIClient({
appId: 'your app id',
retryOptions: {
maxRetries: 5,
retryDelay: 2000,
retryStatusCodes: [503, 504], // 只对 503 和 504 重试
},
});自定义重试逻辑
import type { RetryOptions } from '@seaverse/auth-sdk';
const retryOptions: RetryOptions = {
maxRetries: 3,
retryDelay: 1000,
// 自定义判断逻辑:只对特定错误重试
shouldRetry: (error) => {
// 只对服务不可用错误重试
if (error.response?.status === 503) {
return true;
}
// 对没有响应的网络错误重试
if (!error.response) {
return true;
}
return false;
},
};
const client = new SeaVerseBackendAPIClient({
appId: 'your app id',
retryOptions,
});RetryOptions 类型定义
interface RetryOptions {
maxRetries?: number; // 最大重试次数,默认 0(禁用)
retryDelay?: number; // 初始重试延迟(毫秒),默认 1000
retryStatusCodes?: number[]; // 触发重试的状态码列表
shouldRetry?: (error: AxiosError) => boolean; // 自定义重试判断函数
}使用建议:
- ⚠️ 生产环境建议禁用重试(默认行为),避免在业务逻辑错误时产生重复请求
- ✅ 仅在网络不稳定的场景启用重试,如移动端应用、弱网环境
- ✅ 确保后端 API 支持幂等性操作,避免重试导致的副作用
- ✅ 对于关键业务(如支付),建议使用自定义
shouldRetry仔细控制重试逻辑
环境配置
SDK支持以下环境:
| 环境 | 描述 | BaseURL |
|------|------|---------|
| production | 生产环境 | https://account-hub.seaverse.ai |
| staging | 测试环境 | https://api.staging.seaverse.dev |
| development | 开发环境 | https://api.dev.seaverse.dev |
| local | 本地环境 | http://localhost:3000 |
自动检测规则:
*.seaverse.com→ production*.staging.seaverse.dev→ staging*.dev.seaverse.dev→ developmentlocalhost→ local
API 参考
认证相关
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| register() | { email, password, username?, invitation_code?, frontend_url? } | RegisterResponse | 注册新用户,frontend_url 为邮箱验证链接的前端URL,默认为 window.location.href |
| login() | { email, password, frontend_url? } | LoginResponse | 用户登录,frontend_url 用于未验证邮箱时发送验证邮件,默认为 https://seaverse.ai/。⚠️ 如果返回 INVITE_CODE_REQUIRED 且包含 redirectUrl,会自动跳转 |
| getCurrentUser() | - | User | 获取当前用户 |
| logout() | - | SuccessResponse | 登出 |
| verifyEmail() | verifyToken: string | EmailVerificationResponse | 验证邮箱并返回自动登录 token |
| bindInviteCode() | { user_id, invite_code } | BindInviteCodeResponse | 绑定邀请码激活临时账户并自动登录 |
| forgotPassword() | { email, frontend_url? } | SuccessResponse | 忘记密码,frontend_url 默认为 window.location.href |
| resetPassword() | { token, new_password } | SuccessResponse | 重置密码 |
| setToken() | token: string | void | 设置认证 token(OAuth 登录后使用) |
| getApiServiceToken() | - | ApiServiceTokenResponse | 获取API Token |
注册流程说明
register() 方法支持三种注册流程,SDK 会根据后端响应自动处理:
流程 1: 立即激活(默认)
const response = await client.register({ email, password });
// response.success === true
// response.requiresEmailVerification === undefined (或 false)
// response.requiresInvitationCode === undefined (或 false)
// → SDK 自动调用 login() 并触发 onSignupSuccess 回调流程 2: 需要邮箱验证
const response = await client.register({ email, password });
// response.success === true
// response.requiresEmailVerification === true
// → SDK 显示 Toast 提示用户检查邮箱
// → 不会自动调用 login(),用户需要点击邮件中的验证链接流程 3: 需要邀请码激活
const response = await client.register({ email, password });
// response.success === true
// response.requiresInvitationCode === true
// response.tempUserId === "temp_xxx"
// → SDK 显示邀请码输入表单
// → 不会自动调用 login(),用户需要输入邀请码重要提示:
- AuthModal 会自动处理这三种流程,无需手动判断
- 只有在无需验证或激活的情况下,才会自动调用
login() - 这避免了在邮箱未验证或账户未激活时产生不必要的登录请求
OAuth相关
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| googleAuthorize() | { return_url? } | OAuthAuthorizeResponse | Google OAuth 授权 URL |
| discordAuthorize() | { return_url? } | OAuthAuthorizeResponse | Discord OAuth 授权 URL |
| githubAuthorize() | { return_url? } | OAuthAuthorizeResponse | GitHub OAuth 授权 URL |
| unlinkGoogle() | - | SuccessResponse | 解绑Google |
| unlinkDiscord() | - | SuccessResponse | 解绑Discord |
| unlinkGithub() | - | SuccessResponse | 解绑GitHub |
容器管理
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| listContainers() | - | ContainerListResponse | 列出容器 |
| registerContainer() | { containerId, metadata } | SuccessResponse | 注册容器 |
| getContainer() | { containerId } | Container | 获取容器信息 |
| unregisterContainer() | { containerId } | SuccessResponse | 注销容器 |
| containerHeartbeat() | { containerId, status } | SuccessResponse | 容器心跳 |
| getContainerStats() | - | ContainerStatsResponse | 容器统计 |
技能市场
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| listMarketplaceSkills() | { category?, page?, pageSize? } | MarketplaceSkillsListResponse | 列出市场技能 |
| getMarketplaceSkill() | { skillId } | MarketplaceSkill | 获取技能详情 |
| installSkill() | { skillId } | SuccessResponse | 安装技能 |
| listUserSkills() | - | UserInstalledSkillsListResponse | 列出已安装技能 |
| uninstallSkill() | { skillId } | SuccessResponse | 卸载技能 |
邀请码管理
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| applyInvite() | { email, reason } | ApplyInviteResponse | 申请邀请码(reason 需10-500字符)|
| listInvites() | { page?, page_size?, status? } | ListInvitesResponse | 列出我的邀请码 |
| getInviteStats() | - | InviteStatsResponse | 获取邀请码统计 |
| getInvite() | inviteId: string | InviteCodeDetailResponse | 获取邀请码详情 |
| getInviteUsages() | inviteId: string, { page?, page_size? } | ListInviteUsagesResponse | 获取邀请码使用记录 |
其他
| 方法 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| getHealth() | - | HealthResponse | 健康检查 |
| getConversationStatus() | { conversationId } | ConversationStatusResponse | 获取对话状态 |
| getSpeechToken() | - | SpeechTokenResponse | 获取语音Token |
响应格式兼容性
自动响应解包
从 v0.2.5 开始,SDK 自动兼容两种API响应格式,无需手动处理:
格式 1: 包装格式(推荐)
{
"data": {
"token": "eyJhbGc...",
"user": { ... }
},
"success": true
}格式 2: 扁平格式(向后兼容)
{
"token": "eyJhbGc...",
"user": { ... }
}SDK 会自动检测响应格式并提取正确的数据:
// 无论后端返回哪种格式,以下代码都能正常工作
const loginResult = await client.login({
email: '[email protected]',
password: 'password123',
});
console.log(loginResult.token); // ✅ 始终能正确获取 token
console.log(loginResult.user); // ✅ 始终能正确获取 user受影响的方法
以下方法已实现自动响应解包:
- ✅
login()- 登录接口 - ✅
register()- 注册接口 - ✅
getCurrentUser()- 获取当前用户
实现原理
SDK 内部通过以下逻辑自动处理响应格式:
const response = await httpClient.request(config);
const responseData = response.data;
// 检测并解包
if (responseData.data && typeof responseData.data === 'object') {
// 包装格式: 提取 data 字段
return responseData.data;
}
// 扁平格式: 直接返回
return responseData;这意味着:
- 🔄 后端格式变更无需前端修改 - 后端可以自由调整响应格式
- 🔧 渐进式迁移 - 可以逐步将不同接口迁移到新格式
- ✅ 向后兼容 - 旧代码无需修改即可继续工作
错误处理
错误响应格式
所有 API 错误都遵循统一的响应格式:
interface ApiError {
success: false; // 错误时始终为 false
error: string; // 人类可读的错误消息
code?: string; // 机器可读的错误码
details?: any; // 额外的错误详情
}错误码
SDK 提供了标准的错误码枚举:
import { ErrorCode } from '@seaverse/auth-sdk';
// 账户相关错误
ErrorCode.ACCOUNT_EXISTS // 账户已存在
ErrorCode.ACCOUNT_NOT_FOUND // 账户不存在
ErrorCode.ACCOUNT_SUSPENDED // 账户已被暂停
ErrorCode.INVALID_CREDENTIALS // 登录凭证无效
ErrorCode.EMAIL_NOT_VERIFIED // 邮箱未验证
// 验证相关错误
ErrorCode.INVALID_EMAIL // 无效的邮箱地址
ErrorCode.INVALID_PASSWORD // 无效的密码
ErrorCode.PASSWORD_TOO_WEAK // 密码强度不够
// 邀请码相关错误
ErrorCode.INVALID_INVITATION_CODE // 无效的邀请码
ErrorCode.INVITATION_REQUIRED // 需要邀请码
ErrorCode.INVITE_CODE_REQUIRED // 需要邀请码(会自动跳转到 redirectUrl)
// Token 相关错误
ErrorCode.INVALID_TOKEN // 无效的 token
ErrorCode.TOKEN_EXPIRED // Token 已过期
// 内部错误
ErrorCode.INTERNAL_ERROR // 服务器内部错误错误处理最佳实践
1. 注册时处理重复用户
由于后端在账户已存在时返回 200 OK(成功响应),所以前端不会抛出异常,而是在响应体中包含错误信息。
import { ErrorCode } from '@seaverse/auth-sdk';
const result = await client.register({
email: '[email protected]',
password: 'password123',
});
// 检查响应中的 success 字段
if (result.success) {
console.log('注册成功:', result);
// 进行后续操作,如自动登录
} else if (result.code === ErrorCode.ACCOUNT_EXISTS) {
// 账户已存在,提示用户
const { email, app_id } = result.details;
console.log(`账户 ${email} 已存在于应用 ${app_id} 中`);
// 显示友好的提示信息
alert('This email is already registered. Please login instead.');
// 或者引导用户去登录
showLoginModal();
} else {
// 处理其他错误
console.error('注册失败:', result.error);
}2. 登录时处理各种错误
try {
const result = await client.login({
email: '[email protected]',
password: 'wrong-password',
});
console.log('登录成功:', result);
} catch (error) {
const errorCode = error.response?.data?.code;
switch (errorCode) {
case ErrorCode.INVALID_CREDENTIALS:
showError('用户名或密码错误');
break;
case ErrorCode.EMAIL_NOT_VERIFIED:
showError('请先验证您的邮箱');
showResendVerificationButton();
break;
case ErrorCode.ACCOUNT_SUSPENDED:
showError('您的账户已被暂停,请联系管理员');
break;
default:
showError('登录失败,请稍后重试');
}
}3. 通用错误处理函数
import { ErrorCode } from '@seaverse/auth-sdk';
function handleApiError(error: any) {
const apiError = error.response?.data;
if (!apiError) {
// 网络错误或其他未知错误
return {
title: '网络错误',
message: '请检查您的网络连接',
};
}
// 根据错误码返回用户友好的消息
const errorMessages: Record<string, { title: string; message: string }> = {
[ErrorCode.ACCOUNT_EXISTS]: {
title: '账户已存在',
message: '该邮箱已注册,请直接登录',
},
[ErrorCode.INVALID_CREDENTIALS]: {
title: '登录失败',
message: '用户名或密码错误',
},
[ErrorCode.EMAIL_NOT_VERIFIED]: {
title: '邮箱未验证',
message: '请先验证您的邮箱地址',
},
[ErrorCode.INVALID_INVITATION_CODE]: {
title: '邀请码无效',
message: '请检查您的邀请码是否正确',
},
[ErrorCode.TOKEN_EXPIRED]: {
title: '登录已过期',
message: '请重新登录',
},
};
return errorMessages[apiError.code] || {
title: '操作失败',
message: apiError.error || '发生未知错误',
};
}
// 使用示例
try {
await client.register({ email, password });
} catch (error) {
const { title, message } = handleApiError(error);
showNotification(title, message);
}4. TypeScript 类型安全的错误处理
import { ErrorCode, models } from '@seaverse/auth-sdk';
async function safeRegister(email: string, password: string) {
// Register API now returns 200 OK for both success and account exists cases
const result = await client.register({ email, password });
if (result.success) {
return {
success: true,
data: result,
};
} else if (result.code === ErrorCode.ACCOUNT_EXISTS) {
// TypeScript knows the type of details
const details = result.details as models.AccountExistsErrorDetails;
return {
success: false,
error: 'ACCOUNT_EXISTS',
email: details.email,
appId: details.app_id,
};
} else {
return {
success: false,
error: result.error || 'Unknown error',
};
}
}
// Usage
const registerResult = await safeRegister('[email protected]', 'password123');
if (registerResult.success) {
console.log('Registration successful');
} else if (registerResult.error === 'ACCOUNT_EXISTS') {
console.log(`Account already exists: ${registerResult.email}`);
}HTTP 状态码对照
| HTTP状态码 | 错误码示例 | 说明 |
|-----------|----------|------|
| 200 OK | ACCOUNT_EXISTS | 账户已存在(业务错误但返回成功响应) |
| 400 Bad Request | INVALID_EMAIL, PASSWORD_TOO_WEAK | 请求参数无效 |
| 401 Unauthorized | INVALID_CREDENTIALS, TOKEN_EXPIRED | 认证失败 |
| 403 Forbidden | EMAIL_NOT_VERIFIED, ACCOUNT_SUSPENDED | 权限不足 |
| 500 Internal Server Error | INTERNAL_ERROR | 服务器内部错误 |
注意:对于注册接口,账户已存在的情况会返回 200 OK,但在响应体中 success: false 和 code: "ACCOUNT_EXISTS"。这样设计是为了前端能够更容易地处理这种常见的业务场景。
类型定义
// 用户信息
interface User {
id?: string;
app_id?: string | null; // 应用ID(多租户支持)
email?: string;
username?: string;
created_at?: number; // Unix时间戳
email_verified?: boolean; // 邮箱验证状态
google_id?: string | null; // Google账号ID
discord_id?: string | null; // Discord账号ID
github_id?: string | null; // GitHub账号ID
// 已弃用字段(向后兼容)
createdAt?: number; // @deprecated 使用 created_at
emailVerified?: boolean; // @deprecated 使用 email_verified
googleId?: string; // @deprecated 使用 google_id
discordId?: string; // @deprecated 使用 discord_id
githubId?: string; // @deprecated 使用 github_id
}
// 登录响应
interface LoginResponse {
token: string;
user: User;
}
// 注册响应
interface RegisterResponse {
success: boolean;
message?: string;
userId?: string;
// 邮箱验证相关
requiresEmailVerification?: boolean; // 是否需要邮箱验证
// 邀请码激活相关
requiresInvitationCode?: boolean; // 是否需要邀请码激活
tempUserId?: string; // 临时用户ID(需要激活)
// 错误信息
error?: string;
code?: string; // 如 'ACCOUNT_EXISTS'
details?: Record<string, any>;
}
// AuthModal选项
interface AuthModalOptions {
client: SeaVerseBackendAPIClient;
theme?: 'dark' | 'light';
onLoginSuccess?: (token: string, user: any) => void;
onSignupSuccess?: (token: string, user: any) => void;
onInviteCodeRequired?: (userId: string, email: string) => void; // 当需要邀请码激活时的回调
onApplyInviteSuccess?: (applicationId: string, email: string) => void; // 申请邀请码成功的回调
onError?: (error: Error) => void;
returnUrl?: string; // OAuth 登录后返回的 URL,可选,默认为 window.location.href
enableOAuth?: {
google?: boolean; // 启用 Google 登录
discord?: boolean; // 启用 Discord 登录
github?: boolean; // 启用 GitHub 登录
};
}完整示例
查看 examples/auth-sdk-demo 目录获取完整的可运行示例。
# 运行示例
cd examples/auth-sdk-demo
pnpm install
pnpm dev从旧版本迁移
v0.1.x → v0.2.0 升级指南
1. 向后兼容性
好消息!v0.2.0 完全向后兼容,现有代码无需修改即可继续工作。
2. 推荐的迁移步骤
虽然不是必须的,但我们建议逐步迁移到新的字段命名规范:
// 旧代码(仍然可用)
const user = await client.getCurrentUser();
console.log(user.emailVerified); // ⚠️ 已弃用但可用
console.log(user.createdAt); // ⚠️ 已弃用但可用
// 新代码(推荐)
const user = await client.getCurrentUser();
console.log(user.email_verified); // ✅ 推荐使用
console.log(user.created_at); // ✅ 推荐使用
console.log(user.app_id); // ✅ 新字段:多租户支持3. 注册接口更新
// 旧代码(仍然可用)
await client.register({
email: '[email protected]',
password: 'password123',
invitationCode: 'INVITE123', // ⚠️ 已弃用但可用
});
// 新代码(推荐)
await client.register({
email: '[email protected]',
password: 'password123',
username: 'myusername', // ✨ 新功能
invitation_code: 'INVITE123', // ✅ 推荐使用
});4. 密码重置接口更新
// 旧代码(需要更新)
await client.resetPassword({
token: 'reset-token',
newPassword: 'NewPass123', // ⚠️ 已弃用
});
// 新代码(推荐)
await client.resetPassword({
token: 'reset-token',
new_password: 'NewPass123', // ✅ 推荐使用
});5. TypeScript 类型提示
如果你使用 TypeScript,编译器会自动提示已弃用的字段:
const user = await client.getCurrentUser();
user.emailVerified; // TypeScript 会显示删除线和弃用警告
user.email_verified; // ✅ 无警告6. 多租户功能(新增)
如果你的应用需要支持多租户架构,可以使用新的 app_id 字段:
const user = await client.getCurrentUser();
console.log('your app id:', user.app_id);常见问题
如何处理认证token?
// 登录成功后保存token
const loginResult = await client.login({ email, password });
localStorage.setItem('token', loginResult.token);
// 创建带认证的client
import { AuthFactory } from '@seaverse/auth-sdk';
const authenticatedClient = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:应用ID
environment: 'production',
auth: AuthFactory.create({
type: 'jwt',
credentials: {
type: 'jwt',
token: localStorage.getItem('token'),
},
}),
});OAuth redirect_uri_mismatch错误?
确保OAuth应用配置中的重定向URI与代码中的redirectUri完全一致(包括协议、域名、端口、路径)。
如何自定义请求超时?
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:应用ID
environment: 'production',
timeout: 30000, // 30秒
});本地开发如何连接到本地API?
const client = new SeaVerseBackendAPIClient({
appId: 'your app id', // 必需:应用ID
environment: 'local', // 或使用 baseURL: 'http://localhost:3000'
});开发
# 安装依赖
pnpm install
# 构建
pnpm build
# Watch模式
pnpm dev
# 测试
pnpm test相关链接
- 📦 NPM Package
- 📖 示例项目
- 🐛 Issues
- 💬 Discussions
License
MIT © SeaVerse Team
更新日志
v0.3.6 (当前版本)
- 🧹 代码清理: 移除桌面应用OAuth回调相关功能
- 移除
oauthDesktopURL配置选项 - 简化OAuth流程,统一使用
returnUrl参数 - 清理相关文档和代码注释
- 移除
v0.2.5
- 🔄 响应格式兼容: 自动兼容包装格式和扁平格式的API响应
- 修复登录时提示 "Invalid response from server" 的问题
login(),register(),getCurrentUser()方法自动解包data字段- 支持两种格式:
{ data: {...}, success: true }和{ ... }
- 📝 文档更新: 新增响应格式兼容性章节
v0.2.0
- 🔄 API路径更新: 所有认证API从
/api/auth/*迁移到/sdk/v1/auth/* - 🏢 多租户支持: User模型新增
app_id字段,支持多应用隔离 - 🔑 重要变更:
appId现在是必需参数,SDK 自动在所有请求中添加X-App-ID请求头 - ✨ 新功能:
- 注册时支持自定义
username(可选) - 所有字段标准化为 snake_case(保留 camelCase 向后兼容)
- 注册时支持自定义
- 📝 字段更新:
SeaVerseBackendAPIClientOptions: 新增必需字段appIdUser: 新增app_id,created_at,email_verified,google_id,discord_id,github_idRegisterRequest: 新增username,invitation_codeResetPasswordRequest:newPassword→new_password
- ⚠️ Breaking Changes:
- 必须提供
appId参数才能初始化 client - API 路径变更需要后端支持
- 必须提供
- ✅ 向后兼容: 除
appId外,所有旧字段仍然可用
v0.1.5
- 修复OAuth state管理,从sessionStorage切换到localStorage
- 增强API响应处理
v0.1.4
- 增强API响应处理
- 优化错误处理
v0.1.2
- 更新包名为@seaverse/auth-sdk
- 添加多环境支持
查看完整更新日志:CHANGELOG.md
