@cloudbase/auth 3.1.8 → 3.1.9
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/dist/cjs/index.d.ts +4 -3
- package/dist/cjs/index.js +104 -22
- package/dist/cjs/v1-compat.d.ts +77 -0
- package/dist/cjs/v1-compat.js +423 -0
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.js +101 -22
- package/dist/esm/v1-compat.d.ts +77 -0
- package/dist/esm/v1-compat.js +419 -0
- package/dist/miniprogram/index.js +1 -1
- package/package.json +5 -5
- package/src/index.ts +98 -7
- package/src/v1-compat.ts +499 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/auth",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.9",
|
|
4
4
|
"description": "cloudbase javascript sdk auth componets",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"license": "Apache-2.0",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@cloudbase/adapter-wx_mp": "^1.3.1",
|
|
35
|
-
"@cloudbase/oauth": "3.1.
|
|
36
|
-
"@cloudbase/utilities": "3.1.
|
|
35
|
+
"@cloudbase/oauth": "3.1.9",
|
|
36
|
+
"@cloudbase/utilities": "3.1.9"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@cloudbase/types": "3.1.
|
|
39
|
+
"@cloudbase/types": "3.1.9",
|
|
40
40
|
"@types/node": "^22.1.0",
|
|
41
41
|
"terser-webpack-plugin": "^3.0.2",
|
|
42
42
|
"webpack-bundle-analyzer": "^4.9.1"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "d354f0b4080b9cf208a499966328cc33954b3c19"
|
|
45
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,13 @@ import {
|
|
|
29
29
|
useDefaultAdapter,
|
|
30
30
|
} from './utilities'
|
|
31
31
|
import { saveToBrowserSession, getBrowserSession, removeBrowserSession, addUrlSearch } from './utils'
|
|
32
|
+
import {
|
|
33
|
+
AuthV1Compat,
|
|
34
|
+
WeixinAuthProvider,
|
|
35
|
+
CustomAuthProvider,
|
|
36
|
+
AnonymousAuthProvider,
|
|
37
|
+
applyUserV1Compat,
|
|
38
|
+
} from './v1-compat'
|
|
32
39
|
import { utils } from '@cloudbase/utilities'
|
|
33
40
|
import {
|
|
34
41
|
CommonRes,
|
|
@@ -329,6 +336,10 @@ export class User implements IUser {
|
|
|
329
336
|
})
|
|
330
337
|
}
|
|
331
338
|
}
|
|
339
|
+
|
|
340
|
+
// 应用 v1 兼容方法到 User 类
|
|
341
|
+
applyUserV1Compat(User)
|
|
342
|
+
|
|
332
343
|
interface ILoginStateOptions extends IUserOptions {
|
|
333
344
|
envId: string
|
|
334
345
|
}
|
|
@@ -370,7 +381,7 @@ interface IAuthConfig extends ICloudbaseAuthConfig {
|
|
|
370
381
|
runtime?: string
|
|
371
382
|
}
|
|
372
383
|
|
|
373
|
-
class Auth {
|
|
384
|
+
class Auth extends AuthV1Compat {
|
|
374
385
|
readonly config: IAuthConfig
|
|
375
386
|
oauthInstance: CloudbaseOAuth
|
|
376
387
|
readonly cache: ICloudbaseCache
|
|
@@ -378,6 +389,7 @@ class Auth {
|
|
|
378
389
|
private hasListenerSetUp = false
|
|
379
390
|
|
|
380
391
|
constructor(config: IAuthConfig) {
|
|
392
|
+
super()
|
|
381
393
|
this.config = config
|
|
382
394
|
this.oauthInstance = config.oauthInstance
|
|
383
395
|
this.cache = config.cache
|
|
@@ -1124,6 +1136,8 @@ class Auth {
|
|
|
1124
1136
|
callback.call(this, loginState)
|
|
1125
1137
|
}
|
|
1126
1138
|
|
|
1139
|
+
// v1 兼容 API 方法已移至 AuthV1Compat 基类 (v1-compat.ts)
|
|
1140
|
+
|
|
1127
1141
|
/**
|
|
1128
1142
|
* 强制刷新token
|
|
1129
1143
|
* @param params
|
|
@@ -1218,7 +1232,7 @@ class Auth {
|
|
|
1218
1232
|
const { verification_token } = verifyRes
|
|
1219
1233
|
|
|
1220
1234
|
// 手机登录参数
|
|
1221
|
-
let username =
|
|
1235
|
+
let username = this.formatPhone(rawUsername)
|
|
1222
1236
|
let signUpParam: any = { phone_number: username }
|
|
1223
1237
|
|
|
1224
1238
|
// 邮箱登录参数
|
|
@@ -1504,7 +1518,16 @@ class Auth {
|
|
|
1504
1518
|
|
|
1505
1519
|
return { data: { user: session.user, session }, error: null }
|
|
1506
1520
|
} catch (error) {
|
|
1507
|
-
|
|
1521
|
+
const authError = new AuthError(error)
|
|
1522
|
+
// 优化错误提示:登录失败时提供更友好的排查指引
|
|
1523
|
+
if (authError.message?.includes('密码不正确') || authError.message?.includes('password')) {
|
|
1524
|
+
console.warn('[CloudBase Auth] 登录失败提示:\n'
|
|
1525
|
+
+ ' 1. 请确认用户名/邮箱/手机号是否正确\n'
|
|
1526
|
+
+ ' 2. 请确认密码是否正确\n'
|
|
1527
|
+
+ ' 3. 如果用户不存在,请先通过 auth.signUp() 注册用户,或在云开发控制台手动创建用户\n'
|
|
1528
|
+
+ ' 4. 确认当前环境已开启对应的登录方式(控制台 → 环境 → 登录授权)',)
|
|
1529
|
+
}
|
|
1530
|
+
return { data: {}, error: authError }
|
|
1508
1531
|
}
|
|
1509
1532
|
}
|
|
1510
1533
|
|
|
@@ -2457,7 +2480,7 @@ class Auth {
|
|
|
2457
2480
|
return { ...(loginState as any), data: { user: session.user, session }, error: null }
|
|
2458
2481
|
}
|
|
2459
2482
|
|
|
2460
|
-
|
|
2483
|
+
protected formatPhone(phone: string) {
|
|
2461
2484
|
if (!/\s+/.test(phone) && /^\+\d{1,3}\d+/.test(phone)) {
|
|
2462
2485
|
return phone.replace(/^(\+\d{1,2})(\d+)$/, '$1 $2')
|
|
2463
2486
|
}
|
|
@@ -2687,6 +2710,52 @@ export function generateAuthInstance(
|
|
|
2687
2710
|
|
|
2688
2711
|
const NAMESPACE = 'auth'
|
|
2689
2712
|
|
|
2713
|
+
/**
|
|
2714
|
+
* 计算两个字符串之间的编辑距离(Levenshtein distance)
|
|
2715
|
+
* 用于在用户调用不存在的 Auth 方法时,推荐最相似的方法名
|
|
2716
|
+
*/
|
|
2717
|
+
function levenshteinDistance(a: string, b: string): number {
|
|
2718
|
+
const matrix: number[][] = []
|
|
2719
|
+
for (let i = 0; i <= a.length; i++) {
|
|
2720
|
+
matrix[i] = [i]
|
|
2721
|
+
}
|
|
2722
|
+
for (let j = 0; j <= b.length; j++) {
|
|
2723
|
+
matrix[0][j] = j
|
|
2724
|
+
}
|
|
2725
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2726
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2727
|
+
const cost = a[i - 1].toLowerCase() === b[j - 1].toLowerCase() ? 0 : 1
|
|
2728
|
+
matrix[i][j] = Math.min(
|
|
2729
|
+
matrix[i - 1][j] + 1,
|
|
2730
|
+
matrix[i][j - 1] + 1,
|
|
2731
|
+
matrix[i - 1][j - 1] + cost,
|
|
2732
|
+
)
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
return matrix[a.length][b.length]
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
/**
|
|
2739
|
+
* 在方法列表中查找与目标名称最相似的方法
|
|
2740
|
+
* @returns 最相似的方法名,如果没有足够相似的则返回 null
|
|
2741
|
+
*/
|
|
2742
|
+
function findSimilarMethod(target: string, methods: string[]): string | null {
|
|
2743
|
+
let bestMatch: string | null = null
|
|
2744
|
+
let bestDistance = Infinity
|
|
2745
|
+
const maxDistance = Math.max(3, Math.floor(target.length * 0.4)) // 允许 40% 的编辑距离
|
|
2746
|
+
|
|
2747
|
+
for (const method of methods) {
|
|
2748
|
+
// 跳过私有方法和构造函数
|
|
2749
|
+
if (method.startsWith('_') || method === 'constructor') continue
|
|
2750
|
+
const distance = levenshteinDistance(target, method)
|
|
2751
|
+
if (distance < bestDistance && distance <= maxDistance) {
|
|
2752
|
+
bestDistance = distance
|
|
2753
|
+
bestMatch = method
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
return bestMatch
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2690
2759
|
const component: ICloudbaseComponent = {
|
|
2691
2760
|
name: COMPONENT_NAME,
|
|
2692
2761
|
namespace: NAMESPACE,
|
|
@@ -2742,8 +2811,30 @@ const component: ICloudbaseComponent = {
|
|
|
2742
2811
|
const authProto = auth.call(this, config)
|
|
2743
2812
|
Object.assign(auth, authProto)
|
|
2744
2813
|
Object.setPrototypeOf(auth, Object.getPrototypeOf(authProto))
|
|
2745
|
-
|
|
2746
|
-
|
|
2814
|
+
|
|
2815
|
+
// 包装 Proxy,当用户调用不存在的方法时提供 "Did you mean?" 提示
|
|
2816
|
+
const authWithHint = typeof Proxy !== 'undefined'
|
|
2817
|
+
? new Proxy(auth, {
|
|
2818
|
+
get(target, prop, receiver) {
|
|
2819
|
+
const value = Reflect.get(target, prop, receiver)
|
|
2820
|
+
if (value !== undefined || typeof prop !== 'string') {
|
|
2821
|
+
return value
|
|
2822
|
+
}
|
|
2823
|
+
// 查找最相似的方法名
|
|
2824
|
+
const allKeys = Object.getOwnPropertyNames(Object.getPrototypeOf(target))
|
|
2825
|
+
.concat(Object.keys(target))
|
|
2826
|
+
.filter(k => typeof target[k] === 'function')
|
|
2827
|
+
const suggestion = findSimilarMethod(prop, allKeys)
|
|
2828
|
+
if (suggestion) {
|
|
2829
|
+
console.warn(`[CloudBase Auth] auth.${prop} is not a function. Did you mean: auth.${suggestion}() ?`)
|
|
2830
|
+
}
|
|
2831
|
+
return value
|
|
2832
|
+
},
|
|
2833
|
+
})
|
|
2834
|
+
: auth
|
|
2835
|
+
|
|
2836
|
+
this[NAMESPACE] = authWithHint
|
|
2837
|
+
return authWithHint
|
|
2747
2838
|
},
|
|
2748
2839
|
}
|
|
2749
2840
|
|
|
@@ -2753,7 +2844,7 @@ try {
|
|
|
2753
2844
|
cloudbase.registerComponent(component)
|
|
2754
2845
|
} catch (e) {}
|
|
2755
2846
|
|
|
2756
|
-
export { UserInfo, Auth }
|
|
2847
|
+
export { UserInfo, Auth, WeixinAuthProvider, CustomAuthProvider, AnonymousAuthProvider }
|
|
2757
2848
|
/**
|
|
2758
2849
|
* @api 手动注册至cloudbase app
|
|
2759
2850
|
*/
|
package/src/v1-compat.ts
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-useless-constructor */
|
|
2
|
+
/**
|
|
3
|
+
* v1 兼容层
|
|
4
|
+
* 本文件包含 v1 版本的 Provider 类、Auth v1 兼容方法和 User v1 兼容方法。
|
|
5
|
+
* Auth 类通过 extends AuthV1Compat 来继承 v1 方法。
|
|
6
|
+
* User 类通过 applyUserV1Compat mixin 来扩展 v1 方法。
|
|
7
|
+
*/
|
|
8
|
+
import type { authModels } from '@cloudbase/oauth'
|
|
9
|
+
import {
|
|
10
|
+
LOGIN_STATE_CHANGED_TYPE,
|
|
11
|
+
AUTH_STATE_CHANGED_TYPE,
|
|
12
|
+
} from '@cloudbase/oauth'
|
|
13
|
+
import {
|
|
14
|
+
throwError,
|
|
15
|
+
ERRORS,
|
|
16
|
+
} from './utilities'
|
|
17
|
+
import type { SignUpRes } from './type'
|
|
18
|
+
|
|
19
|
+
// ========== v1 兼容 Provider 类 ==========
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* v1 微信登录 Provider
|
|
23
|
+
* 使用方式: auth.weixinAuthProvider({ appid, scope }).signInWithRedirect()
|
|
24
|
+
*
|
|
25
|
+
* @deprecated v1 兼容 API。建议使用 auth.signInWithOAuth({ provider }) 替代。
|
|
26
|
+
*/
|
|
27
|
+
export class WeixinAuthProvider {
|
|
28
|
+
constructor(_options: {
|
|
29
|
+
authInstance: any
|
|
30
|
+
appid: string
|
|
31
|
+
scope: string
|
|
32
|
+
}) {
|
|
33
|
+
// v1 兼容类,所有方法均抛出异常提示替代方案
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated v1 兼容 API。
|
|
38
|
+
* 建议使用 auth.signInWithOAuth({ provider: 'providerId' }) 替代。
|
|
39
|
+
*/
|
|
40
|
+
public signInWithRedirect(): void {
|
|
41
|
+
throw new Error('[v1 兼容] WeixinAuthProvider.signInWithRedirect() 在当前版本中无法实现。'
|
|
42
|
+
+ ' 建议使用 auth.signInWithOAuth({ provider: \'providerId\' }) 替代。',)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated v1 兼容 API。
|
|
47
|
+
* 建议使用 auth.verifyOAuth({ code, state, provider }) 替代。
|
|
48
|
+
*/
|
|
49
|
+
public async getRedirectResult(_options?: {
|
|
50
|
+
createUser?: boolean
|
|
51
|
+
syncUserInfo?: boolean
|
|
52
|
+
}): Promise<any> {
|
|
53
|
+
throw new Error('[v1 兼容] WeixinAuthProvider.getRedirectResult() 在当前版本中无法实现。'
|
|
54
|
+
+ ' 建议使用 auth.verifyOAuth({ code, state, provider }) 替代。',)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @deprecated v1 兼容 API。
|
|
59
|
+
* 建议使用 auth.linkIdentity({ provider: 'providerId' }) 替代。
|
|
60
|
+
*/
|
|
61
|
+
public async getLinkRedirectResult(_options?: {
|
|
62
|
+
withUnionId?: boolean
|
|
63
|
+
}): Promise<void> {
|
|
64
|
+
throw new Error('[v1 兼容] WeixinAuthProvider.getLinkRedirectResult() 在当前版本中无法实现。'
|
|
65
|
+
+ ' 建议使用 auth.linkIdentity({ provider: \'providerId\' }) 替代。',)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* v1 自定义登录 Provider
|
|
71
|
+
* 使用方式: auth.customAuthProvider().signIn(ticket)
|
|
72
|
+
*
|
|
73
|
+
* @deprecated v1 兼容 API。建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。
|
|
74
|
+
*/
|
|
75
|
+
export class CustomAuthProvider {
|
|
76
|
+
private authInstance: any
|
|
77
|
+
|
|
78
|
+
constructor(options: {
|
|
79
|
+
authInstance: any
|
|
80
|
+
}) {
|
|
81
|
+
this.authInstance = options.authInstance
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 使用自定义登录凭据 ticket 登录云开发
|
|
86
|
+
* @param ticket 自定义登录 ticket
|
|
87
|
+
* @deprecated v1 兼容 API。建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。
|
|
88
|
+
*/
|
|
89
|
+
public async signIn(ticket: string): Promise<void> {
|
|
90
|
+
// 使用 Auth 已有的 signInWithCustomTicket 方法
|
|
91
|
+
await this.authInstance.signInWithCustomTicket(() => Promise.resolve(ticket))
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* v1 匿名登录 Provider
|
|
97
|
+
* 使用方式: auth.anonymousAuthProvider().signIn()
|
|
98
|
+
*
|
|
99
|
+
* @deprecated v1 兼容 API。建议使用 auth.signInAnonymously({}) 替代。
|
|
100
|
+
*/
|
|
101
|
+
export class AnonymousAuthProvider {
|
|
102
|
+
private authInstance: any
|
|
103
|
+
|
|
104
|
+
constructor(options: {
|
|
105
|
+
authInstance: any
|
|
106
|
+
}) {
|
|
107
|
+
this.authInstance = options.authInstance
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 匿名登录云开发
|
|
112
|
+
* @deprecated v1 兼容 API。建议使用 auth.signInAnonymously({}) 替代。
|
|
113
|
+
*/
|
|
114
|
+
public async signIn(): Promise<void> {
|
|
115
|
+
// 使用 Auth 已有的 signInAnonymously 方法
|
|
116
|
+
await this.authInstance.signInAnonymously({})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ========== Auth v1 兼容基类 ==========
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* AuthV1Compat 基类,提供 v1 版本的兼容 API 方法。
|
|
124
|
+
* Auth 类通过 extends AuthV1Compat 来继承这些方法。
|
|
125
|
+
*
|
|
126
|
+
* 注意:此类中的方法通过 this 访问子类 Auth 的方法和属性,
|
|
127
|
+
* 包括: signIn, signUp, signInWithCustomTicket, signInAnonymously,
|
|
128
|
+
* getVerification, resetPassword, onLoginStateChanged, onAuthStateChange,
|
|
129
|
+
* createLoginState, formatPhone, config, signInWithOAuth 等。
|
|
130
|
+
*/
|
|
131
|
+
// eslint-disable @typescript-eslint/member-ordering -- abstract 基类中 abstract 声明与 public 方法排序不适用常规规则
|
|
132
|
+
export abstract class AuthV1Compat {
|
|
133
|
+
abstract readonly config: any
|
|
134
|
+
|
|
135
|
+
// ========== v1 兼容 API 方法 ==========
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* v1 API: 获取用于微信登录的 WeixinAuthProvider // cspell:ignore weixin Weixin
|
|
139
|
+
* @deprecated 建议使用 auth.signInWithOAuth({ provider: appid }) 替代。
|
|
140
|
+
*/
|
|
141
|
+
public weixinAuthProvider(options: { appid: string; scope: string }): WeixinAuthProvider {
|
|
142
|
+
return new WeixinAuthProvider({
|
|
143
|
+
authInstance: this,
|
|
144
|
+
appid: options.appid,
|
|
145
|
+
scope: options.scope,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* v1 API: 获取用于自定义登录的 CustomAuthProvider
|
|
151
|
+
* @deprecated 建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。
|
|
152
|
+
*/
|
|
153
|
+
public customAuthProvider(): CustomAuthProvider {
|
|
154
|
+
return new CustomAuthProvider({
|
|
155
|
+
authInstance: this,
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* v1 API: 获取用于匿名登录的 AnonymousAuthProvider
|
|
161
|
+
* @deprecated 建议使用 auth.signInAnonymously({}) 替代。
|
|
162
|
+
*/
|
|
163
|
+
public anonymousAuthProvider(): AnonymousAuthProvider {
|
|
164
|
+
return new AnonymousAuthProvider({
|
|
165
|
+
authInstance: this,
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* v1 API: 使用邮箱和密码注册云开发账户
|
|
171
|
+
* @deprecated 建议使用 auth.signUp({ email, password }) 替代。
|
|
172
|
+
*/
|
|
173
|
+
public async signUpWithEmailAndPassword(email: string, password: string): Promise<SignUpRes> {
|
|
174
|
+
if (typeof email !== 'string') {
|
|
175
|
+
throwError(ERRORS.INVALID_PARAMS, 'email must be a string')
|
|
176
|
+
}
|
|
177
|
+
if (typeof password !== 'string') {
|
|
178
|
+
throwError(ERRORS.INVALID_PARAMS, 'password must be a string')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 使用 Auth 已有的 signUp 方法
|
|
182
|
+
return await this.signUp({
|
|
183
|
+
email,
|
|
184
|
+
password,
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* v1 API: 使用邮箱和密码登录云开发
|
|
190
|
+
* @deprecated 建议使用 auth.signInWithPassword({ email, password }) 替代。
|
|
191
|
+
*/
|
|
192
|
+
public async signInWithEmailAndPassword(email: string, password: string): Promise<any> {
|
|
193
|
+
if (typeof email !== 'string') {
|
|
194
|
+
throwError(ERRORS.INVALID_PARAMS, 'email must be a string')
|
|
195
|
+
}
|
|
196
|
+
if (typeof password !== 'string') {
|
|
197
|
+
throwError(ERRORS.INVALID_PARAMS, 'password must be a string')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 使用 Auth 已有的 signIn 方法
|
|
201
|
+
await this.signIn({
|
|
202
|
+
username: email,
|
|
203
|
+
password,
|
|
204
|
+
})
|
|
205
|
+
return this.createLoginState()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* v1 API: 发送重置密码的邮件
|
|
210
|
+
* @deprecated 建议使用 auth.resetPasswordForEmail(email) 替代。
|
|
211
|
+
*/
|
|
212
|
+
public async sendPasswordResetEmail(email: string): Promise<void> {
|
|
213
|
+
if (typeof email !== 'string') {
|
|
214
|
+
throwError(ERRORS.INVALID_PARAMS, 'email must be a string')
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 使用 Auth 已有的 getVerification 方法
|
|
218
|
+
await this.getVerification({ email })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* v1 API: 使用用户名密码登录云开发
|
|
223
|
+
* @deprecated 建议使用 auth.signInWithPassword({ username, password }) 替代。
|
|
224
|
+
*/
|
|
225
|
+
public async signInWithUsernameAndPassword(username: string, password: string): Promise<any> {
|
|
226
|
+
if (typeof username !== 'string') {
|
|
227
|
+
throwError(ERRORS.INVALID_PARAMS, 'username must be a string')
|
|
228
|
+
}
|
|
229
|
+
if (typeof password !== 'string') {
|
|
230
|
+
throwError(ERRORS.INVALID_PARAMS, 'password must be a string')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 使用 Auth 已有的 signIn 方法
|
|
234
|
+
await this.signIn({
|
|
235
|
+
username,
|
|
236
|
+
password,
|
|
237
|
+
})
|
|
238
|
+
return this.createLoginState()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* v1 API: 发送手机验证码
|
|
243
|
+
* @deprecated 建议使用 auth.signInWithOtp({ phone }) 或 auth.getVerification({ phone_number }) 替代。
|
|
244
|
+
*/
|
|
245
|
+
public async sendPhoneCode(phoneNumber: string): Promise<boolean> {
|
|
246
|
+
if (typeof phoneNumber !== 'string') {
|
|
247
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 使用 Auth 已有的 getVerification 方法
|
|
251
|
+
const formattedPhone = this.formatPhone(phoneNumber)
|
|
252
|
+
await this.getVerification({ phone_number: formattedPhone })
|
|
253
|
+
return true
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* v1 API: 手机号注册(支持短信验证码+密码方式)
|
|
258
|
+
* @deprecated 建议使用 auth.signUp({ phone_number, verification_code, password? }) 替代。
|
|
259
|
+
*/
|
|
260
|
+
public async signUpWithPhoneCode(phoneNumber: string, phoneCode: string, password?: string): Promise<any> {
|
|
261
|
+
if (typeof phoneNumber !== 'string') {
|
|
262
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string')
|
|
263
|
+
}
|
|
264
|
+
if (typeof phoneCode !== 'string') {
|
|
265
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneCode must be a string')
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const formattedPhone = this.formatPhone(phoneNumber)
|
|
269
|
+
|
|
270
|
+
// 使用 Auth 已有的 signUp 方法
|
|
271
|
+
await this.signUp({
|
|
272
|
+
phone_number: formattedPhone,
|
|
273
|
+
verification_code: phoneCode,
|
|
274
|
+
...(password ? { password } : {}),
|
|
275
|
+
})
|
|
276
|
+
return this.createLoginState()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* v1 API: 手机号登录(支持短信验证码 or 密码方式)
|
|
281
|
+
* @deprecated 密码方式建议使用 auth.signInWithPassword({ phone, password }) 替代;
|
|
282
|
+
* 验证码方式建议使用 auth.signInWithOtp({ phone }) 替代。
|
|
283
|
+
*/
|
|
284
|
+
public async signInWithPhoneCodeOrPassword(params: {
|
|
285
|
+
phoneNumber: string
|
|
286
|
+
phoneCode?: string
|
|
287
|
+
password?: string
|
|
288
|
+
}): Promise<any> {
|
|
289
|
+
const { phoneNumber, phoneCode, password } = params
|
|
290
|
+
if (typeof phoneNumber !== 'string') {
|
|
291
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string')
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const formattedPhone = this.formatPhone(phoneNumber)
|
|
295
|
+
|
|
296
|
+
if (password) {
|
|
297
|
+
// 使用 Auth 已有的 signIn 方法进行密码登录
|
|
298
|
+
await this.signIn({
|
|
299
|
+
username: formattedPhone,
|
|
300
|
+
password,
|
|
301
|
+
})
|
|
302
|
+
} else if (phoneCode) {
|
|
303
|
+
// 使用 Auth 已有的 signIn 方法进行验证码登录
|
|
304
|
+
await this.signIn({
|
|
305
|
+
username: formattedPhone,
|
|
306
|
+
verification_token: phoneCode,
|
|
307
|
+
} as any)
|
|
308
|
+
} else {
|
|
309
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneCode or password must be provided')
|
|
310
|
+
}
|
|
311
|
+
return this.createLoginState()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* v1 API: 手机号强制重置密码
|
|
316
|
+
* @deprecated 建议使用 auth.resetPasswordForEmail(phoneNumber) 替代。
|
|
317
|
+
*/
|
|
318
|
+
public async forceResetPwdByPhoneCode(params: {
|
|
319
|
+
phoneNumber: string
|
|
320
|
+
phoneCode: string
|
|
321
|
+
password: string
|
|
322
|
+
}): Promise<any> {
|
|
323
|
+
const { phoneNumber, phoneCode, password } = params
|
|
324
|
+
if (typeof phoneNumber !== 'string') {
|
|
325
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneNumber must be a string')
|
|
326
|
+
}
|
|
327
|
+
if (typeof phoneCode !== 'string') {
|
|
328
|
+
throwError(ERRORS.INVALID_PARAMS, 'phoneCode must be a string')
|
|
329
|
+
}
|
|
330
|
+
if (typeof password !== 'string') {
|
|
331
|
+
throwError(ERRORS.INVALID_PARAMS, 'password must be a string')
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const formattedPhone = this.formatPhone(phoneNumber)
|
|
335
|
+
|
|
336
|
+
// 使用 Auth 已有的 resetPassword 方法
|
|
337
|
+
await this.resetPassword({
|
|
338
|
+
phone_number: formattedPhone,
|
|
339
|
+
new_password: password,
|
|
340
|
+
verification_token: phoneCode,
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// 重置密码成功后使用 Auth 已有的 signIn 方法自动登录
|
|
344
|
+
await this.signIn({
|
|
345
|
+
username: formattedPhone,
|
|
346
|
+
password,
|
|
347
|
+
})
|
|
348
|
+
return this.createLoginState()
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* v1 API: 接收一个回调函数,在刷新短期访问令牌前调用,根据返回值决定是否刷新
|
|
353
|
+
* @deprecated v1 兼容 API,当前 v3 SDK 中无直接对应方法。
|
|
354
|
+
* 建议使用 auth.onAuthStateChange(callback) 监听 TOKEN_REFRESHED 事件替代。
|
|
355
|
+
*/
|
|
356
|
+
public shouldRefreshAccessToken(_callback: () => boolean): void {
|
|
357
|
+
throw new Error('[v1 兼容] shouldRefreshAccessToken() 在当前版本中无法实现。'
|
|
358
|
+
+ ' 建议使用 auth.onAuthStateChange(callback) 来监听 TOKEN_REFRESHED 事件替代。',)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* v1 API: 接收一个回调函数,在登录状态过期时调用
|
|
363
|
+
* @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 SIGNED_OUT 事件。
|
|
364
|
+
*/
|
|
365
|
+
public onLoginStateExpired(callback: Function): void {
|
|
366
|
+
// 使用 Auth 已有的 onLoginStateChanged 方法
|
|
367
|
+
this.onLoginStateChanged((params) => {
|
|
368
|
+
if (params?.eventType === LOGIN_STATE_CHANGED_TYPE.CREDENTIALS_ERROR) {
|
|
369
|
+
callback.call(this)
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* v1 API: 接收一个回调函数,在短期访问令牌刷新后调用
|
|
376
|
+
* @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 TOKEN_REFRESHED 事件。
|
|
377
|
+
*/
|
|
378
|
+
public onAccessTokenRefreshed(callback: Function): void {
|
|
379
|
+
// 使用 Auth 已有的 onAuthStateChange 方法
|
|
380
|
+
this.onAuthStateChange((event) => {
|
|
381
|
+
if (event === AUTH_STATE_CHANGED_TYPE.TOKEN_REFRESHED) {
|
|
382
|
+
callback.call(this)
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* v1 API: 接收一个回调函数,在匿名登录状态被转换后调用
|
|
389
|
+
* @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 SIGNED_IN 事件。
|
|
390
|
+
*/
|
|
391
|
+
public onAnonymousConverted(callback: Function): void {
|
|
392
|
+
// 使用 Auth 已有的 onAuthStateChange 方法
|
|
393
|
+
this.onAuthStateChange((event) => {
|
|
394
|
+
if (event === AUTH_STATE_CHANGED_TYPE.SIGNED_IN) {
|
|
395
|
+
callback.call(this)
|
|
396
|
+
}
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* v1 API: 接收一个回调函数,在登录类型发生变化后调用
|
|
402
|
+
* @deprecated 建议使用 auth.onAuthStateChange(callback) 替代,监听 SIGNED_IN / SIGNED_OUT 事件。
|
|
403
|
+
*/
|
|
404
|
+
public onLoginTypeChanged(callback: Function): void {
|
|
405
|
+
// 使用 Auth 已有的 onAuthStateChange 方法
|
|
406
|
+
this.onAuthStateChange((event) => {
|
|
407
|
+
if (event === AUTH_STATE_CHANGED_TYPE.SIGNED_IN || event === AUTH_STATE_CHANGED_TYPE.SIGNED_OUT) {
|
|
408
|
+
callback.call(this)
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ========== 声明子类 Auth 需要提供的方法/属性 ==========
|
|
414
|
+
abstract signIn(params: authModels.SignInRequest): Promise<any>
|
|
415
|
+
abstract signUp(params: authModels.SignUpRequest & { phone?: string }): Promise<SignUpRes>
|
|
416
|
+
abstract signInWithCustomTicket(getTickFn?: authModels.GetCustomSignTicketFn): Promise<any>
|
|
417
|
+
abstract signInAnonymously(params: any): Promise<any>
|
|
418
|
+
abstract getVerification(params: any, options?: any): Promise<authModels.GetVerificationResponse>
|
|
419
|
+
abstract resetPassword(params: authModels.ResetPasswordRequest): Promise<void>
|
|
420
|
+
abstract onLoginStateChanged(callback: Function): Promise<void>
|
|
421
|
+
abstract onAuthStateChange(callback: any): any
|
|
422
|
+
abstract createLoginState(params?: any, options?: any): Promise<any>
|
|
423
|
+
abstract signInWithOAuth(params: any): Promise<any>
|
|
424
|
+
abstract grantProviderToken(params: authModels.GrantProviderTokenRequest): Promise<authModels.GrantProviderTokenResponse>
|
|
425
|
+
abstract bindWithProvider(params: authModels.BindWithProviderRequest): Promise<void>
|
|
426
|
+
protected abstract formatPhone(phone: string): string
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ========== User v1 兼容 mixin ==========
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* 将 v1 兼容方法应用到 User 类的 prototype 上。
|
|
433
|
+
* 在 index.ts 中调用: applyUserV1Compat(User)
|
|
434
|
+
*/
|
|
435
|
+
export function applyUserV1Compat(UserClass: any): void {
|
|
436
|
+
/**
|
|
437
|
+
* v1 API: 将当前账户与自定义登录 Ticket 进行绑定
|
|
438
|
+
* @deprecated v1 兼容 API,当前版本中无法直接实现。
|
|
439
|
+
* 建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。
|
|
440
|
+
*/
|
|
441
|
+
UserClass.prototype.linkWithTicket = async function (_ticket: string): Promise<void> {
|
|
442
|
+
throw new Error('[v1 兼容] User.linkWithTicket() 在当前版本中无法实现。'
|
|
443
|
+
+ ' 建议使用 auth.signInWithCustomTicket(() => Promise.resolve(ticket)) 替代。',)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* v1 API: 将当前账户与第三方鉴权提供方(以重定向形式)进行绑定
|
|
448
|
+
* @deprecated 建议使用 auth.linkIdentity({ provider: 'providerId' }) 替代。
|
|
449
|
+
*/
|
|
450
|
+
UserClass.prototype.linkWithRedirect = function (_provider: any): void {
|
|
451
|
+
throw new Error('[v1 兼容] User.linkWithRedirect() 在当前版本中无法实现。'
|
|
452
|
+
+ ' 建议使用 auth.linkIdentity({ provider: \'providerId\' }) 替代。',)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* v1 API: 更新当前的登录邮箱
|
|
457
|
+
* @deprecated 建议使用 auth.updateUser({ email: newEmail }) 替代。
|
|
458
|
+
*/
|
|
459
|
+
UserClass.prototype.updateEmail = async function (newEmail: string, _password?: string): Promise<void> {
|
|
460
|
+
if (typeof newEmail !== 'string') {
|
|
461
|
+
throwError(ERRORS.INVALID_PARAMS, 'newEmail must be a string')
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 使用 User 已有的 updateUserBasicInfo 方法
|
|
465
|
+
await this.updateUserBasicInfo({
|
|
466
|
+
email: newEmail,
|
|
467
|
+
})
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* v1 API: 绑定手机号
|
|
472
|
+
* @deprecated v1 兼容 API,当前版本中无法直接在 User 上实现。
|
|
473
|
+
* 建议使用 auth.bindPhoneNumber({ phone_number, verification_token, sudo_token }) 替代。
|
|
474
|
+
*/
|
|
475
|
+
UserClass.prototype.linkWithPhoneNumber = async function (_phoneNumber: string, _phoneCode: string): Promise<void> {
|
|
476
|
+
throw new Error('[v1 兼容] User.linkWithPhoneNumber() 在当前版本中无法实现。'
|
|
477
|
+
+ ' 建议使用 auth.bindPhoneNumber({ phone_number, verification_token, sudo_token }) 替代。',)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* v1 API: 更新手机号
|
|
482
|
+
* @deprecated v1 兼容 API,当前版本中无法直接在 User 上实现。
|
|
483
|
+
* 建议使用 auth.updateUser({ phone: newPhoneNumber }) 替代。
|
|
484
|
+
*/
|
|
485
|
+
UserClass.prototype.updatePhoneNumber = async function (_phoneNumber: string, _phoneCode: string): Promise<void> {
|
|
486
|
+
throw new Error('[v1 兼容] User.updatePhoneNumber() 在当前版本中无法实现。'
|
|
487
|
+
+ ' 建议使用 auth.updateUser({ phone: newPhoneNumber }) 替代。',)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* v1 API: 解绑某个登录方式
|
|
492
|
+
* @deprecated v1 兼容 API,当前版本中无法直接在 User 上实现。
|
|
493
|
+
* 建议使用 auth.unlinkIdentity({ provider: loginType }) 替代。
|
|
494
|
+
*/
|
|
495
|
+
UserClass.prototype.unlink = async function (_loginType: string): Promise<void> {
|
|
496
|
+
throw new Error('[v1 兼容] User.unlink() 在当前版本中无法实现。'
|
|
497
|
+
+ ' 建议使用 auth.unlinkIdentity({ provider: loginType }) 替代。',)
|
|
498
|
+
}
|
|
499
|
+
}
|