@seaverse/auth-sdk 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -92
- package/dist/index.cjs +175 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +112 -57
- package/dist/index.js +176 -200
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,14 @@ const registerResult = await client.register({
|
|
|
96
96
|
invitation_code: 'INVITE123', // 可选
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
// 检查注册结果
|
|
100
|
+
if (registerResult.success) {
|
|
101
|
+
console.log('注册成功:', registerResult);
|
|
102
|
+
} else if (registerResult.code === 'ACCOUNT_EXISTS') {
|
|
103
|
+
console.log('账户已存在,请直接登录');
|
|
104
|
+
console.log('错误详情:', registerResult.details);
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
// 登录
|
|
100
108
|
const loginResult = await client.login({
|
|
101
109
|
email: 'user@example.com',
|
|
@@ -126,44 +134,73 @@ await client.resetPassword({
|
|
|
126
134
|
});
|
|
127
135
|
```
|
|
128
136
|
|
|
129
|
-
### 3. OAuth 第三方登录
|
|
137
|
+
### 3. OAuth 第三方登录 (Backend Proxy Mode)
|
|
138
|
+
|
|
139
|
+
SDK 使用 Backend Proxy Mode,Client Secret 永不暴露给前端,安全性更高。
|
|
140
|
+
|
|
141
|
+
**优势**:
|
|
142
|
+
- ✅ Client Secret 从不暴露给前端
|
|
143
|
+
- ✅ 支持任意开发者域名(无需在 OAuth 平台配置)
|
|
144
|
+
- ✅ 内置 CSRF 防护
|
|
145
|
+
- ✅ 零配置,开箱即用
|
|
130
146
|
|
|
131
|
-
|
|
147
|
+
**工作流程**:
|
|
148
|
+
1. 前端调用 `{provider}Authorize()` 获取 OAuth URL
|
|
149
|
+
2. 前端重定向用户到 OAuth 提供商
|
|
150
|
+
3. 用户授权后,OAuth 提供商回调到固定的 account-hub URL
|
|
151
|
+
4. account-hub 处理 OAuth,创建 JWT token
|
|
152
|
+
5. account-hub 302 重定向到 `return_url?token=xxx`
|
|
153
|
+
6. 前端从 URL 提取 token 并存储
|
|
154
|
+
|
|
155
|
+
#### 使用示例
|
|
156
|
+
|
|
157
|
+
**方式1:使用默认 return_url(当前页面)**
|
|
132
158
|
|
|
133
159
|
```typescript
|
|
134
|
-
//
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
redirectUri: 'https://yourdomain.com/auth/callback',
|
|
138
|
-
});
|
|
160
|
+
// Google 登录
|
|
161
|
+
const { authorize_url } = await client.googleAuthorize();
|
|
162
|
+
window.location.href = authorize_url;
|
|
139
163
|
|
|
140
|
-
|
|
164
|
+
// Discord 登录
|
|
165
|
+
const { authorize_url } = await client.discordAuthorize();
|
|
166
|
+
window.location.href = authorize_url;
|
|
141
167
|
|
|
142
|
-
//
|
|
143
|
-
await client.
|
|
168
|
+
// GitHub 登录
|
|
169
|
+
const { authorize_url } = await client.githubAuthorize();
|
|
170
|
+
window.location.href = authorize_url;
|
|
144
171
|
```
|
|
145
172
|
|
|
146
|
-
|
|
173
|
+
**方式2:自定义 return_url**
|
|
147
174
|
|
|
148
175
|
```typescript
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
176
|
+
// 登录后跳转到 dashboard
|
|
177
|
+
const { authorize_url } = await client.googleAuthorize({
|
|
178
|
+
return_url: 'https://mygame.com/dashboard'
|
|
152
179
|
});
|
|
180
|
+
window.location.href = authorize_url;
|
|
181
|
+
```
|
|
153
182
|
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
**在回调页面提取 token**:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// URL: https://mygame.com/?token=eyJhbGc...
|
|
187
|
+
const token = new URLSearchParams(window.location.search).get('token');
|
|
188
|
+
if (token) {
|
|
189
|
+
localStorage.setItem('token', token);
|
|
190
|
+
// 登录成功,跳转或更新 UI
|
|
191
|
+
}
|
|
156
192
|
```
|
|
157
193
|
|
|
158
|
-
####
|
|
194
|
+
#### OAuth 账号解绑
|
|
159
195
|
|
|
160
196
|
```typescript
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
197
|
+
// 解绑 Google 账号
|
|
198
|
+
await client.unlinkGoogle();
|
|
199
|
+
|
|
200
|
+
// 解绑 Discord 账号
|
|
201
|
+
await client.unlinkDiscord();
|
|
165
202
|
|
|
166
|
-
// 解绑GitHub账号
|
|
203
|
+
// 解绑 GitHub 账号
|
|
167
204
|
await client.unlinkGithub();
|
|
168
205
|
```
|
|
169
206
|
|
|
@@ -189,7 +226,7 @@ const client = new SeaVerseBackendAPIClient({
|
|
|
189
226
|
environment: 'production',
|
|
190
227
|
});
|
|
191
228
|
|
|
192
|
-
//
|
|
229
|
+
// 创建登录弹窗
|
|
193
230
|
const authModal = new AuthModal({
|
|
194
231
|
client,
|
|
195
232
|
theme: 'dark', // 'dark' | 'light' - 默认为 'dark'
|
|
@@ -211,20 +248,12 @@ const authModal = new AuthModal({
|
|
|
211
248
|
console.error('认证错误:', error.message);
|
|
212
249
|
},
|
|
213
250
|
|
|
214
|
-
// OAuth配置(可选)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
discord: {
|
|
221
|
-
clientId: 'YOUR_DISCORD_CLIENT_ID', // 必填
|
|
222
|
-
redirectUri: window.location.origin, // 可选,默认为 window.location.origin
|
|
223
|
-
},
|
|
224
|
-
github: {
|
|
225
|
-
clientId: 'YOUR_GITHUB_CLIENT_ID', // 必填
|
|
226
|
-
redirectUri: window.location.origin, // 可选,默认为 window.location.origin
|
|
227
|
-
},
|
|
251
|
+
// OAuth 配置(可选)
|
|
252
|
+
returnUrl: 'https://mygame.com/', // OAuth 登录后返回的 URL,可选,默认为 window.location.origin
|
|
253
|
+
enableOAuth: {
|
|
254
|
+
google: true, // 启用 Google 登录
|
|
255
|
+
discord: true, // 启用 Discord 登录
|
|
256
|
+
github: true, // 启用 GitHub 登录
|
|
228
257
|
},
|
|
229
258
|
});
|
|
230
259
|
|
|
@@ -239,64 +268,98 @@ authModal.hide();
|
|
|
239
268
|
|
|
240
269
|
// 销毁弹窗
|
|
241
270
|
authModal.destroy();
|
|
271
|
+
```
|
|
272
|
+
|
|
242
273
|
#### OAuth 配置说明
|
|
243
274
|
|
|
244
|
-
`
|
|
275
|
+
`enableOAuth` 参数是**完全可选的**:
|
|
245
276
|
|
|
246
|
-
- 如果**不提供** `
|
|
247
|
-
-
|
|
277
|
+
- 如果**不提供** `enableOAuth`,则不会显示任何第三方登录按钮
|
|
278
|
+
- 如果**部分配置**(如只启用 Google),则只显示已启用的按钮
|
|
248
279
|
- 如果**完整配置**所有平台,则显示所有第三方登录按钮
|
|
249
280
|
|
|
250
281
|
**配置字段说明**:
|
|
251
|
-
- `
|
|
252
|
-
- `
|
|
253
|
-
- `
|
|
282
|
+
- `returnUrl`:**可选** - OAuth 登录后返回的 URL,不填则默认为 `window.location.origin`
|
|
283
|
+
- `enableOAuth.google`:是否启用 Google 登录
|
|
284
|
+
- `enableOAuth.discord`:是否启用 Discord 登录
|
|
285
|
+
- `enableOAuth.github`:是否启用 GitHub 登录
|
|
254
286
|
|
|
255
287
|
```typescript
|
|
256
288
|
// 示例1:无OAuth按钮
|
|
257
|
-
const client1 = new SeaVerseBackendAPIClient({ appId: 'your app id' });
|
|
258
289
|
const authModal1 = new AuthModal({
|
|
259
|
-
client
|
|
290
|
+
client,
|
|
260
291
|
theme: 'dark',
|
|
261
|
-
// 不传
|
|
292
|
+
// 不传 enableOAuth,不显示任何OAuth按钮
|
|
262
293
|
});
|
|
263
294
|
|
|
264
295
|
// 示例2:只显示Google登录
|
|
265
|
-
const client2 = new SeaVerseBackendAPIClient({ appId: 'your app id' });
|
|
266
296
|
const authModal2 = new AuthModal({
|
|
267
|
-
client
|
|
297
|
+
client,
|
|
268
298
|
theme: 'light',
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
},
|
|
274
|
-
// Discord 和 GitHub 未配置,不会显示这些按钮
|
|
299
|
+
returnUrl: 'https://mygame.com/dashboard',
|
|
300
|
+
enableOAuth: {
|
|
301
|
+
google: true,
|
|
302
|
+
// Discord 和 GitHub 未启用,不会显示这些按钮
|
|
275
303
|
},
|
|
276
304
|
});
|
|
277
305
|
|
|
278
|
-
// 示例3:显示所有OAuth
|
|
279
|
-
const client3 = new SeaVerseBackendAPIClient({ appId: 'your app id' });
|
|
306
|
+
// 示例3:显示所有OAuth按钮
|
|
280
307
|
const authModal3 = new AuthModal({
|
|
281
|
-
client
|
|
308
|
+
client,
|
|
282
309
|
theme: 'dark',
|
|
283
|
-
|
|
284
|
-
google:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
},
|
|
288
|
-
discord: {
|
|
289
|
-
clientId: '...'
|
|
290
|
-
// redirectUri 可选,不填则默认为 window.location.origin
|
|
291
|
-
},
|
|
292
|
-
github: {
|
|
293
|
-
clientId: '...'
|
|
294
|
-
// redirectUri 可选,不填则默认为 window.location.origin
|
|
295
|
-
},
|
|
310
|
+
enableOAuth: {
|
|
311
|
+
google: true,
|
|
312
|
+
discord: true,
|
|
313
|
+
github: true,
|
|
296
314
|
},
|
|
297
315
|
});
|
|
298
316
|
```
|
|
299
317
|
|
|
318
|
+
#### 处理 OAuth 回调
|
|
319
|
+
|
|
320
|
+
在 Backend Proxy Mode 下,OAuth 登录后会重定向到 `returnUrl?token=xxx`。在页面加载时检查并处理token:
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// 在页面加载时自动处理 OAuth 回调
|
|
324
|
+
const result = AuthModal.handleOAuthCallback({
|
|
325
|
+
client,
|
|
326
|
+
onLoginSuccess: (token) => {
|
|
327
|
+
localStorage.setItem('token', token);
|
|
328
|
+
console.log('OAuth 登录成功');
|
|
329
|
+
|
|
330
|
+
// 现在可以直接调用需要认证的接口
|
|
331
|
+
// handleOAuthCallback 已自动调用 client.setToken()
|
|
332
|
+
client.getCurrentUser()
|
|
333
|
+
.then(user => console.log('用户信息:', user));
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (result) {
|
|
338
|
+
console.log('处理了 OAuth 回调,token:', result.token);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**重要说明**:
|
|
343
|
+
- `handleOAuthCallback()` 会自动调用 `client.setToken(token)` 来更新 client 的认证配置
|
|
344
|
+
- 这意味着在 `onLoginSuccess` 回调之后,所有需要认证的 API(如 `getCurrentUser()`、`logout()` 等)都会自动带上 `Authorization` header
|
|
345
|
+
|
|
346
|
+
#### 手动设置 Token
|
|
347
|
+
|
|
348
|
+
如果你不使用 `AuthModal.handleOAuthCallback()`,也可以手动设置 token:
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// 从 URL 提取 token
|
|
352
|
+
const token = new URLSearchParams(window.location.search).get('token');
|
|
353
|
+
if (token) {
|
|
354
|
+
// 手动设置 token
|
|
355
|
+
client.setToken(token);
|
|
356
|
+
localStorage.setItem('token', token);
|
|
357
|
+
|
|
358
|
+
// 现在可以调用需要认证的接口
|
|
359
|
+
const user = await client.getCurrentUser();
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
300
363
|
### 5. 容器管理
|
|
301
364
|
|
|
302
365
|
```typescript
|
|
@@ -462,15 +525,16 @@ SDK支持以下环境:
|
|
|
462
525
|
| `logout()` | - | `SuccessResponse` | 登出 |
|
|
463
526
|
| `forgotPassword()` | `{ email }` | `SuccessResponse` | 忘记密码 |
|
|
464
527
|
| `resetPassword()` | `{ token, new_password }` | `SuccessResponse` | 重置密码 |
|
|
528
|
+
| `setToken()` | `token: string` | `void` | 设置认证 token(OAuth 登录后使用) |
|
|
465
529
|
| `getApiServiceToken()` | - | `ApiServiceTokenResponse` | 获取API Token |
|
|
466
530
|
|
|
467
531
|
### OAuth相关
|
|
468
532
|
|
|
469
533
|
| 方法 | 参数 | 返回值 | 说明 |
|
|
470
534
|
|------|------|--------|------|
|
|
471
|
-
| `
|
|
472
|
-
| `
|
|
473
|
-
| `
|
|
535
|
+
| `googleAuthorize()` | `{ return_url? }` | `OAuthAuthorizeResponse` | Google OAuth 授权 URL |
|
|
536
|
+
| `discordAuthorize()` | `{ return_url? }` | `OAuthAuthorizeResponse` | Discord OAuth 授权 URL |
|
|
537
|
+
| `githubAuthorize()` | `{ return_url? }` | `OAuthAuthorizeResponse` | GitHub OAuth 授权 URL |
|
|
474
538
|
| `unlinkGoogle()` | - | `SuccessResponse` | 解绑Google |
|
|
475
539
|
| `unlinkDiscord()` | - | `SuccessResponse` | 解绑Discord |
|
|
476
540
|
| `unlinkGithub()` | - | `SuccessResponse` | 解绑GitHub |
|
|
@@ -504,6 +568,222 @@ SDK支持以下环境:
|
|
|
504
568
|
| `getConversationStatus()` | `{ conversationId }` | `ConversationStatusResponse` | 获取对话状态 |
|
|
505
569
|
| `getSpeechToken()` | - | `SpeechTokenResponse` | 获取语音Token |
|
|
506
570
|
|
|
571
|
+
## 错误处理
|
|
572
|
+
|
|
573
|
+
### 错误响应格式
|
|
574
|
+
|
|
575
|
+
所有 API 错误都遵循统一的响应格式:
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
interface ApiError {
|
|
579
|
+
success: false; // 错误时始终为 false
|
|
580
|
+
error: string; // 人类可读的错误消息
|
|
581
|
+
code?: string; // 机器可读的错误码
|
|
582
|
+
details?: any; // 额外的错误详情
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### 错误码
|
|
587
|
+
|
|
588
|
+
SDK 提供了标准的错误码枚举:
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
import { ErrorCode } from '@seaverse/auth-sdk';
|
|
592
|
+
|
|
593
|
+
// 账户相关错误
|
|
594
|
+
ErrorCode.ACCOUNT_EXISTS // 账户已存在
|
|
595
|
+
ErrorCode.ACCOUNT_NOT_FOUND // 账户不存在
|
|
596
|
+
ErrorCode.ACCOUNT_SUSPENDED // 账户已被暂停
|
|
597
|
+
ErrorCode.INVALID_CREDENTIALS // 登录凭证无效
|
|
598
|
+
ErrorCode.EMAIL_NOT_VERIFIED // 邮箱未验证
|
|
599
|
+
|
|
600
|
+
// 验证相关错误
|
|
601
|
+
ErrorCode.INVALID_EMAIL // 无效的邮箱地址
|
|
602
|
+
ErrorCode.INVALID_PASSWORD // 无效的密码
|
|
603
|
+
ErrorCode.PASSWORD_TOO_WEAK // 密码强度不够
|
|
604
|
+
|
|
605
|
+
// 邀请码相关错误
|
|
606
|
+
ErrorCode.INVALID_INVITATION_CODE // 无效的邀请码
|
|
607
|
+
ErrorCode.INVITATION_REQUIRED // 需要邀请码
|
|
608
|
+
|
|
609
|
+
// Token 相关错误
|
|
610
|
+
ErrorCode.INVALID_TOKEN // 无效的 token
|
|
611
|
+
ErrorCode.TOKEN_EXPIRED // Token 已过期
|
|
612
|
+
|
|
613
|
+
// 内部错误
|
|
614
|
+
ErrorCode.INTERNAL_ERROR // 服务器内部错误
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### 错误处理最佳实践
|
|
618
|
+
|
|
619
|
+
#### 1. 注册时处理重复用户
|
|
620
|
+
|
|
621
|
+
由于后端在账户已存在时返回 200 OK(成功响应),所以前端不会抛出异常,而是在响应体中包含错误信息。
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
import { ErrorCode } from '@seaverse/auth-sdk';
|
|
625
|
+
|
|
626
|
+
const result = await client.register({
|
|
627
|
+
email: 'user@example.com',
|
|
628
|
+
password: 'password123',
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// 检查响应中的 success 字段
|
|
632
|
+
if (result.success) {
|
|
633
|
+
console.log('注册成功:', result);
|
|
634
|
+
// 进行后续操作,如自动登录
|
|
635
|
+
} else if (result.code === ErrorCode.ACCOUNT_EXISTS) {
|
|
636
|
+
// 账户已存在,提示用户
|
|
637
|
+
const { email, app_id } = result.details;
|
|
638
|
+
console.log(`账户 ${email} 已存在于应用 ${app_id} 中`);
|
|
639
|
+
|
|
640
|
+
// 显示友好的提示信息
|
|
641
|
+
alert('This email is already registered. Please login instead.');
|
|
642
|
+
|
|
643
|
+
// 或者引导用户去登录
|
|
644
|
+
showLoginModal();
|
|
645
|
+
} else {
|
|
646
|
+
// 处理其他错误
|
|
647
|
+
console.error('注册失败:', result.error);
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
#### 2. 登录时处理各种错误
|
|
652
|
+
|
|
653
|
+
```typescript
|
|
654
|
+
try {
|
|
655
|
+
const result = await client.login({
|
|
656
|
+
email: 'user@example.com',
|
|
657
|
+
password: 'wrong-password',
|
|
658
|
+
});
|
|
659
|
+
console.log('登录成功:', result);
|
|
660
|
+
} catch (error) {
|
|
661
|
+
const errorCode = error.response?.data?.code;
|
|
662
|
+
|
|
663
|
+
switch (errorCode) {
|
|
664
|
+
case ErrorCode.INVALID_CREDENTIALS:
|
|
665
|
+
showError('用户名或密码错误');
|
|
666
|
+
break;
|
|
667
|
+
case ErrorCode.EMAIL_NOT_VERIFIED:
|
|
668
|
+
showError('请先验证您的邮箱');
|
|
669
|
+
showResendVerificationButton();
|
|
670
|
+
break;
|
|
671
|
+
case ErrorCode.ACCOUNT_SUSPENDED:
|
|
672
|
+
showError('您的账户已被暂停,请联系管理员');
|
|
673
|
+
break;
|
|
674
|
+
default:
|
|
675
|
+
showError('登录失败,请稍后重试');
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### 3. 通用错误处理函数
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import { ErrorCode } from '@seaverse/auth-sdk';
|
|
684
|
+
|
|
685
|
+
function handleApiError(error: any) {
|
|
686
|
+
const apiError = error.response?.data;
|
|
687
|
+
|
|
688
|
+
if (!apiError) {
|
|
689
|
+
// 网络错误或其他未知错误
|
|
690
|
+
return {
|
|
691
|
+
title: '网络错误',
|
|
692
|
+
message: '请检查您的网络连接',
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// 根据错误码返回用户友好的消息
|
|
697
|
+
const errorMessages: Record<string, { title: string; message: string }> = {
|
|
698
|
+
[ErrorCode.ACCOUNT_EXISTS]: {
|
|
699
|
+
title: '账户已存在',
|
|
700
|
+
message: '该邮箱已注册,请直接登录',
|
|
701
|
+
},
|
|
702
|
+
[ErrorCode.INVALID_CREDENTIALS]: {
|
|
703
|
+
title: '登录失败',
|
|
704
|
+
message: '用户名或密码错误',
|
|
705
|
+
},
|
|
706
|
+
[ErrorCode.EMAIL_NOT_VERIFIED]: {
|
|
707
|
+
title: '邮箱未验证',
|
|
708
|
+
message: '请先验证您的邮箱地址',
|
|
709
|
+
},
|
|
710
|
+
[ErrorCode.INVALID_INVITATION_CODE]: {
|
|
711
|
+
title: '邀请码无效',
|
|
712
|
+
message: '请检查您的邀请码是否正确',
|
|
713
|
+
},
|
|
714
|
+
[ErrorCode.TOKEN_EXPIRED]: {
|
|
715
|
+
title: '登录已过期',
|
|
716
|
+
message: '请重新登录',
|
|
717
|
+
},
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
return errorMessages[apiError.code] || {
|
|
721
|
+
title: '操作失败',
|
|
722
|
+
message: apiError.error || '发生未知错误',
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// 使用示例
|
|
727
|
+
try {
|
|
728
|
+
await client.register({ email, password });
|
|
729
|
+
} catch (error) {
|
|
730
|
+
const { title, message } = handleApiError(error);
|
|
731
|
+
showNotification(title, message);
|
|
732
|
+
}
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
#### 4. TypeScript 类型安全的错误处理
|
|
736
|
+
|
|
737
|
+
```typescript
|
|
738
|
+
import { ErrorCode, models } from '@seaverse/auth-sdk';
|
|
739
|
+
|
|
740
|
+
async function safeRegister(email: string, password: string) {
|
|
741
|
+
// Register API now returns 200 OK for both success and account exists cases
|
|
742
|
+
const result = await client.register({ email, password });
|
|
743
|
+
|
|
744
|
+
if (result.success) {
|
|
745
|
+
return {
|
|
746
|
+
success: true,
|
|
747
|
+
data: result,
|
|
748
|
+
};
|
|
749
|
+
} else if (result.code === ErrorCode.ACCOUNT_EXISTS) {
|
|
750
|
+
// TypeScript knows the type of details
|
|
751
|
+
const details = result.details as models.AccountExistsErrorDetails;
|
|
752
|
+
return {
|
|
753
|
+
success: false,
|
|
754
|
+
error: 'ACCOUNT_EXISTS',
|
|
755
|
+
email: details.email,
|
|
756
|
+
appId: details.app_id,
|
|
757
|
+
};
|
|
758
|
+
} else {
|
|
759
|
+
return {
|
|
760
|
+
success: false,
|
|
761
|
+
error: result.error || 'Unknown error',
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Usage
|
|
767
|
+
const registerResult = await safeRegister('user@example.com', 'password123');
|
|
768
|
+
if (registerResult.success) {
|
|
769
|
+
console.log('Registration successful');
|
|
770
|
+
} else if (registerResult.error === 'ACCOUNT_EXISTS') {
|
|
771
|
+
console.log(`Account already exists: ${registerResult.email}`);
|
|
772
|
+
}
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### HTTP 状态码对照
|
|
776
|
+
|
|
777
|
+
| HTTP状态码 | 错误码示例 | 说明 |
|
|
778
|
+
|-----------|----------|------|
|
|
779
|
+
| 200 OK | `ACCOUNT_EXISTS` | 账户已存在(业务错误但返回成功响应) |
|
|
780
|
+
| 400 Bad Request | `INVALID_EMAIL`, `PASSWORD_TOO_WEAK` | 请求参数无效 |
|
|
781
|
+
| 401 Unauthorized | `INVALID_CREDENTIALS`, `TOKEN_EXPIRED` | 认证失败 |
|
|
782
|
+
| 403 Forbidden | `EMAIL_NOT_VERIFIED`, `ACCOUNT_SUSPENDED` | 权限不足 |
|
|
783
|
+
| 500 Internal Server Error | `INTERNAL_ERROR` | 服务器内部错误 |
|
|
784
|
+
|
|
785
|
+
**注意**:对于注册接口,账户已存在的情况会返回 200 OK,但在响应体中 `success: false` 和 `code: "ACCOUNT_EXISTS"`。这样设计是为了前端能够更容易地处理这种常见的业务场景。
|
|
786
|
+
|
|
507
787
|
## 类型定义
|
|
508
788
|
|
|
509
789
|
```typescript
|
|
@@ -547,25 +827,11 @@ interface AuthModalOptions {
|
|
|
547
827
|
onLoginSuccess?: (token: string, user: any) => void;
|
|
548
828
|
onSignupSuccess?: (token: string, user: any) => void;
|
|
549
829
|
onError?: (error: Error) => void;
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
//
|
|
554
|
-
|
|
555
|
-
google?: {
|
|
556
|
-
clientId: string; // 必填
|
|
557
|
-
redirectUri?: string; // 可选,默认为 window.location.origin
|
|
558
|
-
scope?: string; // 可选,默认为 'openid email profile'
|
|
559
|
-
};
|
|
560
|
-
discord?: {
|
|
561
|
-
clientId: string; // 必填
|
|
562
|
-
redirectUri?: string; // 可选,默认为 window.location.origin
|
|
563
|
-
scope?: string; // 可选,默认为 'identify email'
|
|
564
|
-
};
|
|
565
|
-
github?: {
|
|
566
|
-
clientId: string; // 必填
|
|
567
|
-
redirectUri?: string; // 可选,默认为 window.location.origin
|
|
568
|
-
scope?: string; // 可选,默认为 'read:user user:email'
|
|
830
|
+
returnUrl?: string; // OAuth 登录后返回的 URL,可选,默认为 window.location.origin
|
|
831
|
+
enableOAuth?: {
|
|
832
|
+
google?: boolean; // 启用 Google 登录
|
|
833
|
+
discord?: boolean; // 启用 Discord 登录
|
|
834
|
+
github?: boolean; // 启用 GitHub 登录
|
|
569
835
|
};
|
|
570
836
|
}
|
|
571
837
|
```
|