@cloudbase/auth 2.25.1 → 2.25.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/dist/cjs/index.d.ts +72 -2
- package/dist/cjs/index.js +1600 -258
- package/dist/esm/index.d.ts +72 -2
- package/dist/esm/index.js +1601 -259
- package/dist/miniprogram/index.js +1 -1
- package/package.json +5 -5
- package/src/index.ts +1266 -4
- package/dist/cjs/sbaseApi.d.ts +0 -117
- package/dist/cjs/sbaseApi.js +0 -1529
- package/dist/esm/sbaseApi.d.ts +0 -117
- package/dist/esm/sbaseApi.js +0 -1525
- package/src/sbaseApi.ts +0 -1500
package/src/sbaseApi.ts
DELETED
|
@@ -1,1500 +0,0 @@
|
|
|
1
|
-
import type { ICloudbaseAuthConfig } from '@cloudbase/types/auth'
|
|
2
|
-
import type { authModels, CloudbaseOAuth, Credentials } from '@cloudbase/oauth'
|
|
3
|
-
import type { ICloudbaseCache } from '@cloudbase/types/cache'
|
|
4
|
-
import {
|
|
5
|
-
weappJwtDecodeAll,
|
|
6
|
-
EVENTS,
|
|
7
|
-
AUTH_STATE_CHANGED_TYPE,
|
|
8
|
-
AuthError,
|
|
9
|
-
OAUTH_TYPE,
|
|
10
|
-
LOGIN_STATE_CHANGED_TYPE,
|
|
11
|
-
} from '@cloudbase/oauth'
|
|
12
|
-
import {
|
|
13
|
-
CommonRes,
|
|
14
|
-
DeleteMeReq,
|
|
15
|
-
GetClaimsRes,
|
|
16
|
-
GetUserIdentitiesRes,
|
|
17
|
-
GetUserRes,
|
|
18
|
-
LinkIdentityReq,
|
|
19
|
-
LinkIdentityRes,
|
|
20
|
-
OnAuthStateChangeCallback,
|
|
21
|
-
ReauthenticateRes,
|
|
22
|
-
ResendReq,
|
|
23
|
-
ResendRes,
|
|
24
|
-
ResetPasswordForEmailRes,
|
|
25
|
-
ResetPasswordForOldReq,
|
|
26
|
-
SetSessionReq,
|
|
27
|
-
SignInAnonymouslyReq,
|
|
28
|
-
SignInOAuthRes,
|
|
29
|
-
SignInRes,
|
|
30
|
-
SignInWithIdTokenReq,
|
|
31
|
-
SignInWithOAuthReq,
|
|
32
|
-
SignInWithOtpReq,
|
|
33
|
-
SignInWithOtpRes,
|
|
34
|
-
SignInWithPasswordReq,
|
|
35
|
-
SignUpRes,
|
|
36
|
-
UnlinkIdentityReq,
|
|
37
|
-
UpdateUserAttributes,
|
|
38
|
-
UpdateUserReq,
|
|
39
|
-
UpdateUserWithVerificationRes,
|
|
40
|
-
VerifyOAuthReq,
|
|
41
|
-
VerifyOtpReq,
|
|
42
|
-
} from './type'
|
|
43
|
-
import { LoginState, User } from '.'
|
|
44
|
-
import { saveToBrowserSession, getBrowserSession, removeBrowserSession, addUrlSearch } from './utils'
|
|
45
|
-
import { utils } from '@cloudbase/utilities'
|
|
46
|
-
import { adapterForWxMp } from './utilities'
|
|
47
|
-
export const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
48
|
-
|
|
49
|
-
declare const wx: any
|
|
50
|
-
|
|
51
|
-
export class SbaseApi {
|
|
52
|
-
readonly config: ICloudbaseAuthConfig
|
|
53
|
-
oauthInstance: CloudbaseOAuth
|
|
54
|
-
readonly cache: ICloudbaseCache
|
|
55
|
-
private listeners: Map<string, Set<OnAuthStateChangeCallback>> = new Map()
|
|
56
|
-
private hasListenerSetUp = false
|
|
57
|
-
|
|
58
|
-
constructor(config: ICloudbaseAuthConfig & { cache: ICloudbaseCache }) {
|
|
59
|
-
this.config = config
|
|
60
|
-
this.oauthInstance = config.oauthInstance
|
|
61
|
-
this.cache = config.cache
|
|
62
|
-
this.init()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 获取当前登录的用户信息-同步
|
|
67
|
-
*/
|
|
68
|
-
get currentUser() {
|
|
69
|
-
const loginState = this.hasLoginState()
|
|
70
|
-
|
|
71
|
-
if (loginState) {
|
|
72
|
-
return loginState.user || null
|
|
73
|
-
}
|
|
74
|
-
return null
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
hasLoginState(): LoginState | null {
|
|
78
|
-
throw new Error('Auth.hasLoginState() is not implemented')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async setAccessKey() {
|
|
82
|
-
throw new Error('Auth.setAccessKey() is not implemented')
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async signIn(_params: authModels.SignInRequest): Promise<LoginState> {
|
|
86
|
-
throw new Error('Auth.signIn() is not implemented')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async signInWithProvider(_params: authModels.SignInWithProviderRequest): Promise<LoginState> {
|
|
90
|
-
throw new Error('Auth.signInWithProvider() is not implemented')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async genProviderRedirectUri(_params: authModels.GenProviderRedirectUriRequest,): Promise<authModels.GenProviderRedirectUriResponse> {
|
|
94
|
-
throw new Error('Auth.genProviderRedirectUri() is not implemented')
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async getAccessToken(): Promise<{
|
|
98
|
-
accessToken: any
|
|
99
|
-
env: string
|
|
100
|
-
}> {
|
|
101
|
-
throw new Error('Auth.getAccessToken() is not implemented')
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async getUserInfo(): Promise<(authModels.UserInfo & Partial<User>) | null> {
|
|
105
|
-
throw new Error('Auth.getUserInfo() is not implemented')
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async updateUserBasicInfo(_params: authModels.ModifyUserBasicInfoRequest) {
|
|
109
|
-
throw new Error('Auth.updateUserBasicInfo() is not implemented')
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
setCustomSignFunc(_getTickFn: authModels.GetCustomSignTicketFn): void {
|
|
113
|
-
throw new Error('Auth.setCustomSignFunc() is not implemented')
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async grantProviderToken(_params: authModels.GrantProviderTokenRequest,): Promise<authModels.GrantProviderTokenResponse> {
|
|
117
|
-
throw new Error('Auth.grantProviderToken() is not implemented')
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async bindWithProvider(_params: authModels.BindWithProviderRequest): Promise<void> {
|
|
121
|
-
throw new Error('Auth.bindWithProvider() is not implemented')
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async signInWithUsername(_params: {
|
|
125
|
-
verificationInfo?: { verification_id: string; is_user: boolean }
|
|
126
|
-
verificationCode?: string
|
|
127
|
-
username?: string // 用户名称,长度 5-24 位,支持字符中英文、数字、特殊字符(仅支持_-),不支持中文
|
|
128
|
-
bindInfo?: any
|
|
129
|
-
loginType?: string
|
|
130
|
-
autoSignUp?: boolean
|
|
131
|
-
}): Promise<LoginState> {
|
|
132
|
-
throw new Error('Auth.signInWithUsername() is not implemented')
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async getVerification(
|
|
136
|
-
_params: authModels.GetVerificationRequest,
|
|
137
|
-
_options?: { withCaptcha: boolean },
|
|
138
|
-
): Promise<authModels.GetVerificationResponse> {
|
|
139
|
-
throw new Error('Auth.getVerification() is not implemented')
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async createLoginState(
|
|
143
|
-
_params?: { version?: string; query?: any },
|
|
144
|
-
_options?: { asyncRefreshUser?: boolean },
|
|
145
|
-
): Promise<LoginState> {
|
|
146
|
-
throw new Error('Auth.createLoginState() is not implemented')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async verify(_params: authModels.VerifyRequest): Promise<authModels.VerifyResponse> {
|
|
150
|
-
throw new Error('Auth.verify() is not implemented')
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* https://supabase.com/docs/reference/javascript/auth-signinanonymously
|
|
155
|
-
* Sign in a user anonymously.
|
|
156
|
-
* const { data, error } = await auth.signInAnonymously();
|
|
157
|
-
* @param params
|
|
158
|
-
* @returns Promise<SignInRes>
|
|
159
|
-
*/
|
|
160
|
-
async signInAnonymously(params: SignInAnonymouslyReq): Promise<SignInRes> {
|
|
161
|
-
try {
|
|
162
|
-
await this.oauthInstance.authApi.signInAnonymously(params)
|
|
163
|
-
const loginState = await this.createLoginState()
|
|
164
|
-
|
|
165
|
-
const { data: { session } = {} } = await this.getSession()
|
|
166
|
-
|
|
167
|
-
// loginState返回是为了兼容v2版本
|
|
168
|
-
return { ...(loginState as any), data: { user: session.user, session }, error: null }
|
|
169
|
-
} catch (error) {
|
|
170
|
-
return { data: {}, error: new AuthError(error) }
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* https://supabase.com/docs/reference/javascript/auth-signup
|
|
176
|
-
* Sign up a new user with email or phone using a one-time password (OTP). If the account not exist, a new account will be created.
|
|
177
|
-
* let signUpResolve = null;
|
|
178
|
-
const res = await app.auth.signUp({
|
|
179
|
-
phone: "xxxxxx",
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
signUpResolve = res.data.verify
|
|
183
|
-
|
|
184
|
-
const res = await signUpResolve(code);
|
|
185
|
-
|
|
186
|
-
// res.data.callback支持 messageId 参数用于重新发送验证码进行验证,messageId 可以从 resend() 方法中获取
|
|
187
|
-
const { data } = await app.auth.resend({
|
|
188
|
-
type: "signup",
|
|
189
|
-
phone: "xxxxxx",
|
|
190
|
-
})
|
|
191
|
-
const res = await signUpResolve(code, data.messageId);
|
|
192
|
-
* @param params
|
|
193
|
-
* @returns Promise<SignUpRes>
|
|
194
|
-
*/
|
|
195
|
-
async signUp(params: authModels.SignUpRequest): Promise<SignUpRes> {
|
|
196
|
-
if (params.phone_number || params.verification_code || params.verification_token || params.provider_token) {
|
|
197
|
-
await this.oauthInstance.authApi.signUp(params)
|
|
198
|
-
return this.createLoginState() as any
|
|
199
|
-
}
|
|
200
|
-
try {
|
|
201
|
-
// 参数校验:email或phone必填其一
|
|
202
|
-
this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
|
|
203
|
-
|
|
204
|
-
// 第一步:发送验证码并存储 verificationInfo
|
|
205
|
-
const verificationInfo = await this.getVerification(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) },)
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
data: {
|
|
209
|
-
// 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
|
|
210
|
-
verifyOtp: async ({ token, messageId = verificationInfo.verification_id }): Promise<SignInRes> => {
|
|
211
|
-
try {
|
|
212
|
-
// 第三步:待用户输入完验证码之后,验证短信验证码
|
|
213
|
-
const verificationTokenRes = await this.verify({
|
|
214
|
-
verification_id: messageId || verificationInfo.verification_id,
|
|
215
|
-
verification_code: token,
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
// 第四步:注册并登录或直接登录
|
|
219
|
-
// 如果用户已经存在,直接登录
|
|
220
|
-
if (verificationInfo.is_user) {
|
|
221
|
-
await this.signIn({
|
|
222
|
-
username: params.email || this.formatPhone(params.phone),
|
|
223
|
-
verification_token: verificationTokenRes.verification_token,
|
|
224
|
-
})
|
|
225
|
-
} else {
|
|
226
|
-
// 如果用户不存在,注册用户
|
|
227
|
-
const data = JSON.parse(JSON.stringify(params))
|
|
228
|
-
delete data.email
|
|
229
|
-
delete data.phone
|
|
230
|
-
|
|
231
|
-
await this.oauthInstance.authApi.signUp({
|
|
232
|
-
...data,
|
|
233
|
-
...(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) }),
|
|
234
|
-
verification_token: verificationTokenRes.verification_token,
|
|
235
|
-
verification_code: token,
|
|
236
|
-
})
|
|
237
|
-
await this.createLoginState()
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const { data: { session } = {} } = await this.getSession()
|
|
241
|
-
|
|
242
|
-
return { data: { user: session.user, session }, error: null }
|
|
243
|
-
} catch (error) {
|
|
244
|
-
return { data: {}, error: new AuthError(error) }
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
error: null,
|
|
249
|
-
}
|
|
250
|
-
} catch (error) {
|
|
251
|
-
return { data: {}, error: new AuthError(error) }
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* https://supabase.com/docs/reference/javascript/auth-signout
|
|
257
|
-
* const result = await auth.signOut();
|
|
258
|
-
*
|
|
259
|
-
* @param params
|
|
260
|
-
*/
|
|
261
|
-
async signOut(params?: authModels.SignoutRequest): Promise<void | authModels.SignoutReponse> {
|
|
262
|
-
try {
|
|
263
|
-
const { userInfoKey } = this.cache.keys
|
|
264
|
-
const res = await this.oauthInstance.authApi.signOut(params)
|
|
265
|
-
await this.cache.removeStoreAsync(userInfoKey)
|
|
266
|
-
this.setAccessKey()
|
|
267
|
-
|
|
268
|
-
this.config.eventBus?.fire(EVENTS.LOGIN_STATE_CHANGED, { eventType: LOGIN_STATE_CHANGED_TYPE.SIGN_OUT })
|
|
269
|
-
|
|
270
|
-
this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_OUT })
|
|
271
|
-
|
|
272
|
-
// res返回是为了兼容v2版本
|
|
273
|
-
return { ...res, data: {}, error: null }
|
|
274
|
-
} catch (error) {
|
|
275
|
-
return { data: {}, error: new AuthError(error) }
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* https://supabase.com/docs/reference/javascript/auth-onauthstatechange
|
|
281
|
-
* Receive a notification every time an auth event happens. Safe to use without an async function as callback.
|
|
282
|
-
* const { data } = app.auth.onAuthStateChange((event, session) => {
|
|
283
|
-
console.log(event, session);
|
|
284
|
-
if (event === "INITIAL_SESSION") {
|
|
285
|
-
// handle initial session
|
|
286
|
-
} else if (event === "SIGNED_IN") {
|
|
287
|
-
// handle sign in event
|
|
288
|
-
} else if (event === "SIGNED_OUT") {
|
|
289
|
-
// handle sign out event
|
|
290
|
-
} else if (event === "PASSWORD_RECOVERY") {
|
|
291
|
-
// handle password recovery event
|
|
292
|
-
} else if (event === "TOKEN_REFRESHED") {
|
|
293
|
-
// handle token refreshed event
|
|
294
|
-
} else if (event === "USER_UPDATED") {
|
|
295
|
-
// handle user updated event
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
// call unsubscribe to remove the callback
|
|
299
|
-
data.subscription.unsubscribe();
|
|
300
|
-
* @param callback
|
|
301
|
-
* @returns Promise<{ data: { subscription: Subscription }, error: Error | null }>
|
|
302
|
-
*/
|
|
303
|
-
onAuthStateChange(callback: OnAuthStateChangeCallback) {
|
|
304
|
-
if (!this.hasListenerSetUp) {
|
|
305
|
-
this.setupListeners()
|
|
306
|
-
this.hasListenerSetUp = true
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const id = Math.random().toString(36)
|
|
310
|
-
|
|
311
|
-
if (!this.listeners.has(id)) {
|
|
312
|
-
this.listeners.set(id, new Set())
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
this.listeners.get(id)!.add(callback)
|
|
316
|
-
|
|
317
|
-
// 返回 Subscription 对象
|
|
318
|
-
const subscription = {
|
|
319
|
-
id,
|
|
320
|
-
callback,
|
|
321
|
-
unsubscribe: () => {
|
|
322
|
-
const callbacks = this.listeners.get(id)
|
|
323
|
-
if (callbacks) {
|
|
324
|
-
callbacks.delete(callback)
|
|
325
|
-
if (callbacks.size === 0) {
|
|
326
|
-
this.listeners.delete(id)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
},
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
data: { subscription },
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* https://supabase.com/docs/reference/javascript/auth-signinwithpassword
|
|
339
|
-
* Log in an existing user with an email and password or phone and password or username and password.
|
|
340
|
-
* const { data } = await app.auth.signInWithPassword({
|
|
341
|
-
username: "xxx",
|
|
342
|
-
password: "xxx",
|
|
343
|
-
})
|
|
344
|
-
* @param params
|
|
345
|
-
* @returns Promise<SignInRes>
|
|
346
|
-
*/
|
|
347
|
-
async signInWithPassword(params: SignInWithPasswordReq): Promise<SignInRes> {
|
|
348
|
-
try {
|
|
349
|
-
// 参数校验:username/email/phone三选一,password必填
|
|
350
|
-
this.validateAtLeastOne(
|
|
351
|
-
params,
|
|
352
|
-
[['username'], ['email'], ['phone']],
|
|
353
|
-
'You must provide either username, email, or phone',
|
|
354
|
-
)
|
|
355
|
-
this.validateParams(params, {
|
|
356
|
-
password: { required: true, message: 'Password is required' },
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
await this.signIn({
|
|
360
|
-
username: params.username || params.email || this.formatPhone(params.phone),
|
|
361
|
-
password: params.password,
|
|
362
|
-
...(params.is_encrypt ? { isEncrypt: true, version: 'v2' } : {}),
|
|
363
|
-
})
|
|
364
|
-
const { data: { session } = {} } = await this.getSession()
|
|
365
|
-
|
|
366
|
-
return { data: { user: session.user, session }, error: null }
|
|
367
|
-
} catch (error) {
|
|
368
|
-
return { data: {}, error: new AuthError(error) }
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* https://supabase.com/docs/reference/javascript/auth-signinwithidtoken
|
|
374
|
-
* 第三方平台登录。如果用户不存在,会根据云开发平台-登录方式中对应身份源的登录模式配置,判断是否自动注册
|
|
375
|
-
* @param params
|
|
376
|
-
* @returns Promise<SignInRes>
|
|
377
|
-
*/
|
|
378
|
-
async signInWithIdToken(params: SignInWithIdTokenReq): Promise<SignInRes> {
|
|
379
|
-
try {
|
|
380
|
-
// 参数校验:token必填
|
|
381
|
-
this.validateParams(params, {
|
|
382
|
-
token: { required: true, message: 'Token is required' },
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
await this.signInWithProvider({
|
|
386
|
-
provider_token: params.token,
|
|
387
|
-
})
|
|
388
|
-
const { data: { session } = {} } = await this.getSession()
|
|
389
|
-
|
|
390
|
-
return { data: { user: session.user, session }, error: null }
|
|
391
|
-
} catch (error) {
|
|
392
|
-
return { data: {}, error: new AuthError(error) }
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* https://supabase.com/docs/reference/javascript/auth-signinwithotp
|
|
398
|
-
* Log in a user using a one-time password (OTP).
|
|
399
|
-
* let signResolve = null;
|
|
400
|
-
const res = await app.auth.signInWithOtp({
|
|
401
|
-
phone: "xxxxxx",
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
signResolve = res.data.verify
|
|
405
|
-
|
|
406
|
-
const res = await signResolve(code);
|
|
407
|
-
|
|
408
|
-
// res.data.callback支持 messageId 参数用于重新发送验证码进行验证,messageId 可以从 resend() 方法中获取
|
|
409
|
-
const { data } = await app.auth.resend({
|
|
410
|
-
type: "signup",
|
|
411
|
-
phone: "xxxxxx",
|
|
412
|
-
})
|
|
413
|
-
const res = await signUpResolve(code, data.messageId);
|
|
414
|
-
* @param params
|
|
415
|
-
* @returns Promise<SignInWithOtpRes>
|
|
416
|
-
*/
|
|
417
|
-
async signInWithOtp(params: SignInWithOtpReq): Promise<SignInWithOtpRes> {
|
|
418
|
-
try {
|
|
419
|
-
// 参数校验:email或phone必填其一
|
|
420
|
-
this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
|
|
421
|
-
|
|
422
|
-
// 第一步:发送验证码并存储 verificationInfo
|
|
423
|
-
const verificationInfo = await this.getVerification(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) },)
|
|
424
|
-
|
|
425
|
-
return {
|
|
426
|
-
data: {
|
|
427
|
-
user: null,
|
|
428
|
-
session: null,
|
|
429
|
-
// 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
|
|
430
|
-
verifyOtp: async ({ token, messageId = verificationInfo.verification_id }): Promise<SignInRes> => this.verifyOtp({
|
|
431
|
-
email: params.email,
|
|
432
|
-
phone: params.phone,
|
|
433
|
-
token,
|
|
434
|
-
messageId,
|
|
435
|
-
}),
|
|
436
|
-
},
|
|
437
|
-
error: null,
|
|
438
|
-
}
|
|
439
|
-
} catch (error) {
|
|
440
|
-
return { data: {}, error: new AuthError(error) }
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* 校验第三方平台授权登录回调,需先调用 signInWithOAuth 生成第三方平台授权 Uri,访问该 Uri 后,会跳转到第三方平台授权页面,授权完成后回调回来再调用该方法,不传参数则从 url query 和 sessionStorage 中获取
|
|
446
|
-
* // 1. 获取第三方平台授权页地址
|
|
447
|
-
const { data } = app.auth.signInWithOAuth({
|
|
448
|
-
provider: 'string'
|
|
449
|
-
options: {
|
|
450
|
-
redirectTo: 'https://example.com/callback'
|
|
451
|
-
state: 'string'
|
|
452
|
-
}
|
|
453
|
-
})
|
|
454
|
-
|
|
455
|
-
// 2. 访问uri (如 location.href = uri)
|
|
456
|
-
|
|
457
|
-
// 3. 校验第三方平台授权回调
|
|
458
|
-
const { data } = await app.auth.verifyOAuth()
|
|
459
|
-
* @param params
|
|
460
|
-
* @returns Promise<SignInRes>
|
|
461
|
-
*/
|
|
462
|
-
async verifyOAuth(params?: VerifyOAuthReq): Promise<SignInRes | LinkIdentityRes> {
|
|
463
|
-
const data: any = {}
|
|
464
|
-
try {
|
|
465
|
-
// 回调至 provider_redirect_uri 地址(url query中携带 授权code,state等参数),此时检查 state 是否符合预期(如 自己设置的 wx_open)
|
|
466
|
-
const code = params?.code || utils.getQuery('code')
|
|
467
|
-
const state = params?.state || utils.getQuery('state')
|
|
468
|
-
|
|
469
|
-
// 参数校验:code和state必填
|
|
470
|
-
if (!code) {
|
|
471
|
-
return { data: {}, error: new AuthError({ message: 'Code is required' }) }
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
if (!state) {
|
|
475
|
-
return { data: {}, error: new AuthError({ message: 'State is required' }) }
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const cacheData = getBrowserSession(state)
|
|
479
|
-
data.type = cacheData?.type
|
|
480
|
-
|
|
481
|
-
const provider = params?.provider || cacheData?.provider || utils.getQuery('provider')
|
|
482
|
-
|
|
483
|
-
if (!provider) {
|
|
484
|
-
return { data, error: new AuthError({ message: 'Provider is required' }) }
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// state符合预期,则获取该三方平台token
|
|
488
|
-
const { provider_token: token } = await this.grantProviderToken({
|
|
489
|
-
provider_id: provider,
|
|
490
|
-
provider_redirect_uri: location.origin + location.pathname, // 指定三方平台跳回的 url 地址
|
|
491
|
-
provider_code: code, // 第三方平台跳转回页面时,url param 中携带的 code 参数
|
|
492
|
-
})
|
|
493
|
-
|
|
494
|
-
let res: SignInRes | LinkIdentityRes
|
|
495
|
-
|
|
496
|
-
if (cacheData.type === OAUTH_TYPE.BIND_IDENTITY) {
|
|
497
|
-
res = await this.oauthInstance.authApi.toBindIdentity({ provider_token: token, provider, fireEvent: true })
|
|
498
|
-
} else {
|
|
499
|
-
// 通过 provider_token 仅登录或登录并注册(与云开发平台-登录方式-身份源登录模式配置有关)
|
|
500
|
-
res = await this.signInWithIdToken({
|
|
501
|
-
token,
|
|
502
|
-
})
|
|
503
|
-
res.data = { ...data, ...res.data }
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const localSearch = new URLSearchParams(location?.search)
|
|
507
|
-
localSearch.delete('code')
|
|
508
|
-
localSearch.delete('state')
|
|
509
|
-
addUrlSearch(
|
|
510
|
-
cacheData?.search === undefined ? `?${localSearch.toString()}` : cacheData?.search,
|
|
511
|
-
cacheData?.hash || location.hash,
|
|
512
|
-
)
|
|
513
|
-
removeBrowserSession(state)
|
|
514
|
-
|
|
515
|
-
return res
|
|
516
|
-
} catch (error) {
|
|
517
|
-
return { data, error: new AuthError(error) }
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* https://supabase.com/docs/reference/javascript/auth-signinwithoauth
|
|
523
|
-
* 生成第三方平台授权 Uri (如微信二维码扫码授权网页)
|
|
524
|
-
* @param params
|
|
525
|
-
* @returns Promise<SignInOAuthRes>
|
|
526
|
-
*/
|
|
527
|
-
async signInWithOAuth(params: SignInWithOAuthReq): Promise<SignInOAuthRes> {
|
|
528
|
-
try {
|
|
529
|
-
// 参数校验:provider必填
|
|
530
|
-
this.validateParams(params, {
|
|
531
|
-
provider: { required: true, message: 'Provider is required' },
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
const href = params.options?.redirectTo || location.href
|
|
535
|
-
|
|
536
|
-
const urlObject = new URL(href)
|
|
537
|
-
|
|
538
|
-
const provider_redirect_uri = urlObject.origin + urlObject.pathname
|
|
539
|
-
|
|
540
|
-
const state = params.options?.state || `prd-${params.provider}-${Math.random().toString(36)
|
|
541
|
-
.slice(2)}`
|
|
542
|
-
|
|
543
|
-
const { uri } = await this.genProviderRedirectUri({
|
|
544
|
-
provider_id: params.provider,
|
|
545
|
-
provider_redirect_uri,
|
|
546
|
-
state,
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
// 对 URL 进行解码
|
|
550
|
-
const decodedUri = decodeURIComponent(uri)
|
|
551
|
-
|
|
552
|
-
// 合并额外的查询参数
|
|
553
|
-
let finalUri = decodedUri
|
|
554
|
-
|
|
555
|
-
if (params.options?.queryParams) {
|
|
556
|
-
const url = new URL(decodedUri)
|
|
557
|
-
Object.entries(params.options.queryParams).forEach(([key, value]) => {
|
|
558
|
-
url.searchParams.set(key, value)
|
|
559
|
-
})
|
|
560
|
-
finalUri = url.toString()
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
saveToBrowserSession(state, {
|
|
564
|
-
provider: params.provider,
|
|
565
|
-
search: urlObject.search,
|
|
566
|
-
hash: urlObject.hash,
|
|
567
|
-
type: params.options?.type || OAUTH_TYPE.SIGN_IN,
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
if (isBrowser() && !params.options?.skipBrowserRedirect) {
|
|
571
|
-
window.location.assign(finalUri)
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
return { data: { url: finalUri, provider: params.provider }, error: null }
|
|
575
|
-
} catch (error) {
|
|
576
|
-
return { data: {}, error: new AuthError(error) }
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// https://supabase.com/docs/reference/javascript/auth-signinwithsso
|
|
581
|
-
async signInWithSSO() {
|
|
582
|
-
//
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// https://supabase.com/docs/reference/javascript/auth-signinwithweb3
|
|
586
|
-
async signInWithWeb3() {
|
|
587
|
-
//
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// https://supabase.com/docs/reference/javascript/auth-getclaims
|
|
591
|
-
async getClaims(): Promise<GetClaimsRes> {
|
|
592
|
-
try {
|
|
593
|
-
const { accessToken } = await this.getAccessToken()
|
|
594
|
-
const parsedToken = weappJwtDecodeAll(accessToken)
|
|
595
|
-
return { data: parsedToken, error: null }
|
|
596
|
-
} catch (error) {
|
|
597
|
-
return { data: {}, error: new AuthError(error) }
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* https://supabase.com/docs/reference/javascript/auth-resetpasswordforemail
|
|
603
|
-
* 通过 email 或手机号重置密码
|
|
604
|
-
* const { data } = await app.auth.resetPasswordForEmail("xxx@xx.com");
|
|
605
|
-
* const { data } = await app.auth.resetPasswordForEmail("13800138000");
|
|
606
|
-
|
|
607
|
-
await data.updateUser(code, newPassWord);
|
|
608
|
-
*
|
|
609
|
-
* @param emailOrPhone 邮箱或手机号
|
|
610
|
-
* @returns Promise<ResetPasswordForEmailRes>
|
|
611
|
-
*/
|
|
612
|
-
async resetPasswordForEmail(
|
|
613
|
-
emailOrPhone: string,
|
|
614
|
-
options?: { redirectTo?: string },
|
|
615
|
-
): Promise<ResetPasswordForEmailRes> {
|
|
616
|
-
try {
|
|
617
|
-
// 参数校验:emailOrPhone必填
|
|
618
|
-
this.validateParams(
|
|
619
|
-
{ emailOrPhone },
|
|
620
|
-
{
|
|
621
|
-
emailOrPhone: { required: true, message: 'Email or phone is required' },
|
|
622
|
-
},
|
|
623
|
-
)
|
|
624
|
-
|
|
625
|
-
const { redirectTo } = options || {}
|
|
626
|
-
|
|
627
|
-
// 判断是邮箱还是手机号
|
|
628
|
-
const isEmail = emailOrPhone.includes('@')
|
|
629
|
-
let verificationParams: { email?: string; phone_number?: string }
|
|
630
|
-
|
|
631
|
-
if (isEmail) {
|
|
632
|
-
verificationParams = { email: emailOrPhone }
|
|
633
|
-
} else {
|
|
634
|
-
// 正规化手机号
|
|
635
|
-
const formattedPhone = this.formatPhone(emailOrPhone)
|
|
636
|
-
verificationParams = { phone_number: formattedPhone }
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// 第一步:发送验证码并存储 verificationInfo
|
|
640
|
-
const verificationInfo = await this.getVerification(verificationParams)
|
|
641
|
-
|
|
642
|
-
return {
|
|
643
|
-
data: {
|
|
644
|
-
// 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
|
|
645
|
-
updateUser: async (attributes: UpdateUserAttributes): Promise<SignInRes> => {
|
|
646
|
-
this.validateParams(attributes, {
|
|
647
|
-
nonce: { required: true, message: 'Nonce is required' },
|
|
648
|
-
password: { required: true, message: 'Password is required' },
|
|
649
|
-
})
|
|
650
|
-
try {
|
|
651
|
-
// 第三步:待用户输入完验证码之后,验证验证码
|
|
652
|
-
const verificationTokenRes = await this.verify({
|
|
653
|
-
verification_id: verificationInfo.verification_id,
|
|
654
|
-
verification_code: attributes.nonce,
|
|
655
|
-
})
|
|
656
|
-
|
|
657
|
-
await this.oauthInstance.authApi.resetPassword({
|
|
658
|
-
email: isEmail ? emailOrPhone : undefined,
|
|
659
|
-
phone: !isEmail ? emailOrPhone : undefined,
|
|
660
|
-
new_password: attributes.password,
|
|
661
|
-
verification_token: verificationTokenRes.verification_token,
|
|
662
|
-
})
|
|
663
|
-
|
|
664
|
-
this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.PASSWORD_RECOVERY })
|
|
665
|
-
|
|
666
|
-
const res = await this.signInWithPassword({
|
|
667
|
-
email: isEmail ? emailOrPhone : undefined,
|
|
668
|
-
phone: !isEmail ? emailOrPhone : undefined,
|
|
669
|
-
password: attributes.password,
|
|
670
|
-
})
|
|
671
|
-
|
|
672
|
-
if (redirectTo && isBrowser()) {
|
|
673
|
-
window.location.assign(redirectTo)
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
return res
|
|
677
|
-
} catch (error) {
|
|
678
|
-
return { data: {}, error: new AuthError(error) }
|
|
679
|
-
}
|
|
680
|
-
},
|
|
681
|
-
},
|
|
682
|
-
error: null,
|
|
683
|
-
}
|
|
684
|
-
} catch (error) {
|
|
685
|
-
return { data: {}, error: new AuthError(error) }
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
/**
|
|
690
|
-
* 通过旧密码重置密码
|
|
691
|
-
* @param new_password
|
|
692
|
-
* @param old_password
|
|
693
|
-
* @returns
|
|
694
|
-
*/
|
|
695
|
-
async resetPasswordForOld(params: ResetPasswordForOldReq) {
|
|
696
|
-
try {
|
|
697
|
-
await this.oauthInstance.authApi.updatePasswordByOld({
|
|
698
|
-
old_password: params.old_password,
|
|
699
|
-
new_password: params.new_password,
|
|
700
|
-
})
|
|
701
|
-
|
|
702
|
-
const { data: { session } = {} } = await this.getSession()
|
|
703
|
-
|
|
704
|
-
return { data: { user: session.user, session }, error: null }
|
|
705
|
-
} catch (error) {
|
|
706
|
-
return { data: {}, error: new AuthError(error) }
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* https://supabase.com/docs/reference/javascript/auth-verifyotp
|
|
712
|
-
* Log in a user given a User supplied OTP and verificationId received through mobile or email.
|
|
713
|
-
* const verificationInfo = await app.auth.getVerification({
|
|
714
|
-
phone: "xxxxxx",
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
const data = await app.auth.verifyOtp({
|
|
718
|
-
type: "sms",
|
|
719
|
-
phone: "xxxxxx",
|
|
720
|
-
token: "xxxxxx",
|
|
721
|
-
verificationInfo,
|
|
722
|
-
})
|
|
723
|
-
* @param params
|
|
724
|
-
* @returns Promise<SignInRes>
|
|
725
|
-
*/
|
|
726
|
-
async verifyOtp(params: VerifyOtpReq): Promise<SignInRes> {
|
|
727
|
-
try {
|
|
728
|
-
const { type } = params
|
|
729
|
-
// 参数校验:token和verificationInfo必填
|
|
730
|
-
this.validateParams(params, {
|
|
731
|
-
token: { required: true, message: 'Token is required' },
|
|
732
|
-
messageId: { required: true, message: 'messageId is required' },
|
|
733
|
-
})
|
|
734
|
-
|
|
735
|
-
if (['phone_change', 'email_change'].includes(type)) {
|
|
736
|
-
await this.verify({
|
|
737
|
-
verification_id: params.messageId,
|
|
738
|
-
verification_code: params.token,
|
|
739
|
-
})
|
|
740
|
-
} else {
|
|
741
|
-
await this.signInWithUsername({
|
|
742
|
-
verificationInfo: { verification_id: params.messageId, is_user: true },
|
|
743
|
-
verificationCode: params.token,
|
|
744
|
-
username: params.email || this.formatPhone(params.phone) || '',
|
|
745
|
-
loginType: params.email ? 'email' : 'phone',
|
|
746
|
-
})
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
const { data: { session } = {} } = await this.getSession()
|
|
750
|
-
|
|
751
|
-
return { data: { user: session.user, session }, error: null }
|
|
752
|
-
} catch (error) {
|
|
753
|
-
return { data: {}, error: new AuthError(error) }
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* https://supabase.com/docs/reference/javascript/auth-getSession
|
|
759
|
-
* Returns the session, refreshing it if necessary.
|
|
760
|
-
* The session returned can be null if the session is not detected which can happen in the event a user is not signed-in or has logged out.
|
|
761
|
-
* const { data } = await app.auth.getSession()
|
|
762
|
-
* @returns Promise<SignInRes>
|
|
763
|
-
*/
|
|
764
|
-
async getSession(): Promise<SignInRes> {
|
|
765
|
-
try {
|
|
766
|
-
const credentials: Credentials = await this.oauthInstance.oauth2client.getCredentials()
|
|
767
|
-
|
|
768
|
-
if (!credentials || credentials.scope === 'accessKey') {
|
|
769
|
-
return { data: { session: null }, error: null }
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const { data: { user } = {} } = await this.getUser()
|
|
773
|
-
|
|
774
|
-
return { data: { session: { ...credentials, user } }, error: null }
|
|
775
|
-
} catch (error) {
|
|
776
|
-
return { data: {}, error: new AuthError(error) }
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/**
|
|
781
|
-
* https://supabase.com/docs/reference/javascript/auth-refreshsession
|
|
782
|
-
* 无论过期状态如何,都返回一个新的会话。接受一个可选的refresh_token。如果未传入当前会话,则refreshSession()将尝试从getSession()中检索它。如果当前会话的刷新令牌无效,则会抛出一个错误。
|
|
783
|
-
* const { data } = await app.auth.refreshSession()
|
|
784
|
-
* @param refresh_token
|
|
785
|
-
* @returns Promise<SignInRes>
|
|
786
|
-
*/
|
|
787
|
-
async refreshSession(refresh_token?: string): Promise<SignInRes> {
|
|
788
|
-
try {
|
|
789
|
-
const credentials: Credentials = await this.oauthInstance.oauth2client.localCredentials.getCredentials()
|
|
790
|
-
credentials.refresh_token = refresh_token || credentials.refresh_token
|
|
791
|
-
const newTokens = await this.oauthInstance.oauth2client.refreshToken(credentials)
|
|
792
|
-
const { data: { user } = {} } = await this.getUser()
|
|
793
|
-
|
|
794
|
-
return { data: { user, session: { ...newTokens, user } }, error: null }
|
|
795
|
-
} catch (error) {
|
|
796
|
-
return { data: {}, error: new AuthError(error) }
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* https://supabase.com/docs/reference/javascript/auth-getuser
|
|
802
|
-
* 如果存在现有会话,则获取当前用户详细信息。此方法会向服务器发起网络请求,因此返回的值是真实的,可用于制定授权规则。
|
|
803
|
-
* const { data } = await app.auth.getUser()
|
|
804
|
-
* @returns Promise<GetUserRes>
|
|
805
|
-
*/
|
|
806
|
-
async getUser(): Promise<GetUserRes> {
|
|
807
|
-
try {
|
|
808
|
-
const user = this.convertToUser(await this.getUserInfo())
|
|
809
|
-
return { data: { user }, error: null }
|
|
810
|
-
} catch (error) {
|
|
811
|
-
return { data: {}, error: new AuthError(error) }
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
/**
|
|
816
|
-
* 刷新用户信息
|
|
817
|
-
* @returns Promise<CommonRes>
|
|
818
|
-
*/
|
|
819
|
-
async refreshUser(): Promise<CommonRes> {
|
|
820
|
-
try {
|
|
821
|
-
await this.currentUser.refresh()
|
|
822
|
-
|
|
823
|
-
const { data: { session } = {} } = await this.getSession()
|
|
824
|
-
return { data: { user: session.user, session }, error: null }
|
|
825
|
-
} catch (error) {
|
|
826
|
-
return { data: {}, error: new AuthError(error) }
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* https://supabase.com/docs/reference/javascript/auth-updateuser
|
|
832
|
-
* 更新用户信息
|
|
833
|
-
* - 不支持更新密码,更新密码请使用 resetPasswordForEmail 或 reauthenticate
|
|
834
|
-
* - 如果更新 email 或 phone,需要先发送验证码,然后调用返回的 verifyOtp 回调进行验证
|
|
835
|
-
*
|
|
836
|
-
* // 更新不需要验证的字段
|
|
837
|
-
* const { data } = await app.auth.updateUser({
|
|
838
|
-
username: "xxx",
|
|
839
|
-
description: "xxx",
|
|
840
|
-
avatar_url: "xxx",
|
|
841
|
-
nickname: "xxx",
|
|
842
|
-
gender: 'MALE'
|
|
843
|
-
})
|
|
844
|
-
*
|
|
845
|
-
* // 更新 email 或 phone(需要验证)
|
|
846
|
-
* const { data } = await app.auth.updateUser({
|
|
847
|
-
email: "new@example.com"
|
|
848
|
-
})
|
|
849
|
-
* // 调用 verifyOtp 回调验证
|
|
850
|
-
* await data.verifyOtp({ email: "new@example.com", token: "123456" })
|
|
851
|
-
*
|
|
852
|
-
* @param params
|
|
853
|
-
* @returns Promise<GetUserRes | UpdateUserWithVerificationRes>
|
|
854
|
-
*/
|
|
855
|
-
async updateUser(params: UpdateUserReq): Promise<GetUserRes | UpdateUserWithVerificationRes> {
|
|
856
|
-
try {
|
|
857
|
-
// 参数校验:至少有一个更新字段被提供
|
|
858
|
-
const hasValue = Object.keys(params).some(key => params[key] !== undefined && params[key] !== null && params[key] !== '',)
|
|
859
|
-
if (!hasValue) {
|
|
860
|
-
throw new AuthError({ message: 'At least one field must be provided for update' })
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
const { email, phone, ...restParams } = params
|
|
864
|
-
|
|
865
|
-
// 检查是否需要更新 email 或 phone
|
|
866
|
-
const needsEmailVerification = email !== undefined
|
|
867
|
-
const needsPhoneVerification = phone !== undefined
|
|
868
|
-
|
|
869
|
-
let extraRes = {}
|
|
870
|
-
|
|
871
|
-
if (needsEmailVerification || needsPhoneVerification) {
|
|
872
|
-
// 需要发送验证码
|
|
873
|
-
let verificationParams: { email?: string; phone_number?: string }
|
|
874
|
-
let verificationType: 'email_change' | 'phone_change'
|
|
875
|
-
|
|
876
|
-
if (needsEmailVerification) {
|
|
877
|
-
verificationParams = { email: params.email }
|
|
878
|
-
verificationType = 'email_change'
|
|
879
|
-
} else {
|
|
880
|
-
// 正规化手机号
|
|
881
|
-
const formattedPhone = this.formatPhone(params.phone)
|
|
882
|
-
verificationParams = { phone_number: formattedPhone }
|
|
883
|
-
verificationType = 'phone_change'
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
// 发送验证码
|
|
887
|
-
const verificationInfo = await this.getVerification(verificationParams)
|
|
888
|
-
|
|
889
|
-
Object.keys(restParams).length > 0 && await this.updateUserBasicInfo(restParams)
|
|
890
|
-
|
|
891
|
-
extraRes = {
|
|
892
|
-
messageId: verificationInfo.verification_id,
|
|
893
|
-
verifyOtp: async (verifyParams: { email?: string; phone?: string; token: string }): Promise<GetUserRes> => {
|
|
894
|
-
try {
|
|
895
|
-
if (verifyParams.email && params.email === verifyParams.email) {
|
|
896
|
-
// 验证码验证
|
|
897
|
-
await this.verifyOtp({
|
|
898
|
-
type: 'email_change',
|
|
899
|
-
email: params.email,
|
|
900
|
-
token: verifyParams.token,
|
|
901
|
-
messageId: verificationInfo.verification_id,
|
|
902
|
-
})
|
|
903
|
-
await this.updateUserBasicInfo({ email: params.email })
|
|
904
|
-
} else if (verifyParams.phone && params.phone === verifyParams.phone) {
|
|
905
|
-
// 验证码验证
|
|
906
|
-
await this.verifyOtp({
|
|
907
|
-
type: 'phone_change',
|
|
908
|
-
phone: params.phone,
|
|
909
|
-
token: verifyParams.token,
|
|
910
|
-
messageId: verificationInfo.verification_id,
|
|
911
|
-
})
|
|
912
|
-
await this.updateUserBasicInfo({ phone: this.formatPhone(params.phone) })
|
|
913
|
-
} else {
|
|
914
|
-
await this.verifyOtp({
|
|
915
|
-
type: verificationType,
|
|
916
|
-
email: needsEmailVerification ? params.email : undefined,
|
|
917
|
-
phone: !needsEmailVerification ? params.phone : undefined,
|
|
918
|
-
token: verifyParams.token,
|
|
919
|
-
messageId: verificationInfo.verification_id,
|
|
920
|
-
})
|
|
921
|
-
// 验证成功后更新用户信息
|
|
922
|
-
await this.updateUserBasicInfo(params)
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
const {
|
|
926
|
-
data: { user },
|
|
927
|
-
} = await this.getUser()
|
|
928
|
-
this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.USER_UPDATED })
|
|
929
|
-
|
|
930
|
-
return { data: { user }, error: null }
|
|
931
|
-
} catch (error) {
|
|
932
|
-
return { data: {}, error: new AuthError(error) }
|
|
933
|
-
}
|
|
934
|
-
},
|
|
935
|
-
}
|
|
936
|
-
} else {
|
|
937
|
-
// 不需要验证,直接更新
|
|
938
|
-
await this.updateUserBasicInfo(params)
|
|
939
|
-
}
|
|
940
|
-
const {
|
|
941
|
-
data: { user },
|
|
942
|
-
} = await this.getUser()
|
|
943
|
-
this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.USER_UPDATED })
|
|
944
|
-
|
|
945
|
-
return { data: { user, ...extraRes }, error: null }
|
|
946
|
-
} catch (error) {
|
|
947
|
-
return { data: {}, error: new AuthError(error) }
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
/**
|
|
952
|
-
* https://supabase.com/docs/reference/javascript/auth-getuseridentities
|
|
953
|
-
* 获取所有身份源
|
|
954
|
-
* const { data } = await app.auth.getUserIdentities()
|
|
955
|
-
* @returns Promise<GetUserIdentitiesRes>
|
|
956
|
-
*/
|
|
957
|
-
async getUserIdentities(): Promise<GetUserIdentitiesRes> {
|
|
958
|
-
try {
|
|
959
|
-
const providers = await this.oauthInstance.authApi.getProviders()
|
|
960
|
-
|
|
961
|
-
return { data: { identities: providers?.data?.filter(v => !!v.bind) }, error: null }
|
|
962
|
-
} catch (error) {
|
|
963
|
-
return { data: {}, error: new AuthError(error) }
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
/**
|
|
968
|
-
* https://supabase.com/docs/reference/javascript/auth-linkidentity
|
|
969
|
-
* 绑定身份源到当前用户,如果已有用户绑定该身份源,会抛出错误
|
|
970
|
-
* const { data } = await app.auth.linkIdentity({
|
|
971
|
-
provider: 'string'
|
|
972
|
-
})
|
|
973
|
-
* @param params
|
|
974
|
-
* @returns Promise<LinkIdentityRes>
|
|
975
|
-
*/
|
|
976
|
-
async linkIdentity(params: LinkIdentityReq): Promise<LinkIdentityRes> {
|
|
977
|
-
try {
|
|
978
|
-
// 参数校验:provider必填
|
|
979
|
-
this.validateParams(params, {
|
|
980
|
-
provider: { required: true, message: 'Provider is required' },
|
|
981
|
-
})
|
|
982
|
-
|
|
983
|
-
await this.signInWithOAuth({
|
|
984
|
-
provider: params.provider,
|
|
985
|
-
options: {
|
|
986
|
-
type: OAUTH_TYPE.BIND_IDENTITY,
|
|
987
|
-
},
|
|
988
|
-
})
|
|
989
|
-
|
|
990
|
-
return { data: { provider: params.provider }, error: null }
|
|
991
|
-
} catch (error) {
|
|
992
|
-
return { data: {}, error: new AuthError(error) }
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
/**
|
|
997
|
-
* https://supabase.com/docs/reference/javascript/auth-unlinkidentity
|
|
998
|
-
* 解绑身份源
|
|
999
|
-
* const { data } = await app.auth.unlinkIdentity({
|
|
1000
|
-
provider: "xxx"
|
|
1001
|
-
})
|
|
1002
|
-
* @param params
|
|
1003
|
-
* @returns Promise<CommonRes>
|
|
1004
|
-
*/
|
|
1005
|
-
async unlinkIdentity(params: UnlinkIdentityReq): Promise<CommonRes> {
|
|
1006
|
-
try {
|
|
1007
|
-
// 参数校验:provider必填
|
|
1008
|
-
this.validateParams(params, {
|
|
1009
|
-
provider: { required: true, message: 'Provider is required' },
|
|
1010
|
-
})
|
|
1011
|
-
|
|
1012
|
-
await this.oauthInstance.authApi.unbindProvider({ provider_id: params.provider })
|
|
1013
|
-
|
|
1014
|
-
return { data: {}, error: null }
|
|
1015
|
-
} catch (error) {
|
|
1016
|
-
return { data: {}, error: new AuthError(error) }
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* https://supabase.com/docs/reference/javascript/auth-reauthentication
|
|
1022
|
-
* 重新认证。通过发送验证码来重新认证用户身份,支持更新密码
|
|
1023
|
-
* const { data } = await app.auth.reauthenticate()
|
|
1024
|
-
|
|
1025
|
-
await data.updateUser(code, newPassWord)
|
|
1026
|
-
*
|
|
1027
|
-
* @returns Promise<ReauthenticateRes>
|
|
1028
|
-
*/
|
|
1029
|
-
async reauthenticate(): Promise<ReauthenticateRes> {
|
|
1030
|
-
try {
|
|
1031
|
-
const {
|
|
1032
|
-
data: { user },
|
|
1033
|
-
} = await this.getUser()
|
|
1034
|
-
|
|
1035
|
-
this.validateAtLeastOne(user, [['email', 'phone']], 'You must provide either an email or phone number')
|
|
1036
|
-
const userInfo = user.email ? { email: user.email } : { phone_number: this.formatPhone(user.phone) }
|
|
1037
|
-
|
|
1038
|
-
// 第一步:发送验证码并存储 verificationInfo
|
|
1039
|
-
const verificationInfo = await this.getVerification(userInfo)
|
|
1040
|
-
|
|
1041
|
-
return {
|
|
1042
|
-
data: {
|
|
1043
|
-
// 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
|
|
1044
|
-
updateUser: async (attributes: UpdateUserAttributes): Promise<SignInRes> => {
|
|
1045
|
-
this.validateParams(attributes, {
|
|
1046
|
-
nonce: { required: true, message: 'Nonce is required' },
|
|
1047
|
-
})
|
|
1048
|
-
try {
|
|
1049
|
-
if (attributes.password) {
|
|
1050
|
-
// 第三步:待用户输入完验证码之后,验证验证码
|
|
1051
|
-
const verificationTokenRes = await this.verify({
|
|
1052
|
-
verification_id: verificationInfo.verification_id,
|
|
1053
|
-
verification_code: attributes.nonce,
|
|
1054
|
-
})
|
|
1055
|
-
|
|
1056
|
-
// 第四步:获取 sudo_token
|
|
1057
|
-
const sudoRes = await this.oauthInstance.authApi.sudo({
|
|
1058
|
-
verification_token: verificationTokenRes.verification_token,
|
|
1059
|
-
})
|
|
1060
|
-
|
|
1061
|
-
await this.oauthInstance.authApi.setPassword({
|
|
1062
|
-
new_password: attributes.password,
|
|
1063
|
-
sudo_token: sudoRes.sudo_token,
|
|
1064
|
-
})
|
|
1065
|
-
} else {
|
|
1066
|
-
await this.signInWithUsername({
|
|
1067
|
-
verificationInfo,
|
|
1068
|
-
verificationCode: attributes.nonce,
|
|
1069
|
-
...userInfo,
|
|
1070
|
-
loginType: userInfo.email ? 'email' : 'phone',
|
|
1071
|
-
})
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
const { data: { session } = {} } = await this.getSession()
|
|
1075
|
-
|
|
1076
|
-
return { data: { user: session.user, session }, error: null }
|
|
1077
|
-
} catch (error) {
|
|
1078
|
-
return { data: {}, error: new AuthError(error) }
|
|
1079
|
-
}
|
|
1080
|
-
},
|
|
1081
|
-
},
|
|
1082
|
-
error: null,
|
|
1083
|
-
}
|
|
1084
|
-
} catch (error) {
|
|
1085
|
-
return { data: {}, error: new AuthError(error) }
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
/**
|
|
1090
|
-
* https://supabase.com/docs/reference/javascript/auth-resend
|
|
1091
|
-
* 重新发送验证码
|
|
1092
|
-
* - 向用户重新发送邮箱、手机号注册或登录的验证码。
|
|
1093
|
-
* - 可以通过再次调用 signInWithOtp() 方法来重新发送验证码登录。
|
|
1094
|
-
* - 可以通过再次调用 signUp() 方法来重新发送验证码注册。
|
|
1095
|
-
* - 此方法仅在调用 signInWithOtp() 或 signUp() 后,重新发送验证码,收到验证码后再继续调用 signInWithOtp() 或 signUp() 的callback参数,将messageId传入callback。
|
|
1096
|
-
* const result = await app.auth.signInWithOtp({ phone: "xxx" });
|
|
1097
|
-
|
|
1098
|
-
let signResolve = result.data.verify
|
|
1099
|
-
|
|
1100
|
-
const { data } = await app.auth.resend({
|
|
1101
|
-
type: "signup",
|
|
1102
|
-
phone: "xxx",
|
|
1103
|
-
})
|
|
1104
|
-
|
|
1105
|
-
const res = await signResolve(code, data.messageId);
|
|
1106
|
-
* @param params
|
|
1107
|
-
* @returns Promise<ResendRes>
|
|
1108
|
-
*/
|
|
1109
|
-
async resend(params: ResendReq): Promise<ResendRes> {
|
|
1110
|
-
try {
|
|
1111
|
-
// 参数校验:email或phone必填其一
|
|
1112
|
-
this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
|
|
1113
|
-
|
|
1114
|
-
const target = params.type === 'signup' ? 'ANY' : 'USER'
|
|
1115
|
-
const data: { email?: string; phone_number?: string; target: 'USER' | 'ANY' } = { target }
|
|
1116
|
-
if ('email' in params) {
|
|
1117
|
-
data.email = params.email
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
if ('phone' in params) {
|
|
1121
|
-
data.phone_number = this.formatPhone(params.phone)
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// 重新发送验证码
|
|
1125
|
-
const { verification_id: verificationId } = await this.oauthInstance.authApi.getVerification(data)
|
|
1126
|
-
|
|
1127
|
-
return {
|
|
1128
|
-
data: { messageId: verificationId },
|
|
1129
|
-
error: null,
|
|
1130
|
-
}
|
|
1131
|
-
} catch (error: any) {
|
|
1132
|
-
return {
|
|
1133
|
-
data: {},
|
|
1134
|
-
error: new AuthError(error),
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
/**
|
|
1140
|
-
* https://supabase.com/docs/reference/javascript/auth-setsession
|
|
1141
|
-
* 使用access_token和refresh_token来设置会话
|
|
1142
|
-
* 如果成功,则会触发一个SIGNED_IN事件
|
|
1143
|
-
* const { data, error } = await app.auth.setSession({
|
|
1144
|
-
access_token,
|
|
1145
|
-
refresh_token
|
|
1146
|
-
})
|
|
1147
|
-
* @param params
|
|
1148
|
-
* @returns Promise<SignInRes>
|
|
1149
|
-
*/
|
|
1150
|
-
async setSession(params: SetSessionReq): Promise<SignInRes> {
|
|
1151
|
-
try {
|
|
1152
|
-
this.validateParams(params, {
|
|
1153
|
-
access_token: { required: true, message: 'Access token is required' },
|
|
1154
|
-
refresh_token: { required: true, message: 'Refresh token is required' },
|
|
1155
|
-
})
|
|
1156
|
-
|
|
1157
|
-
await this.oauthInstance.oauth2client.refreshToken(params, { throwOnError: true })
|
|
1158
|
-
|
|
1159
|
-
const { data: { session } = {} } = await this.getSession()
|
|
1160
|
-
|
|
1161
|
-
this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_IN })
|
|
1162
|
-
|
|
1163
|
-
return { data: { user: session.user, session }, error: null }
|
|
1164
|
-
} catch (error) {
|
|
1165
|
-
return { data: {}, error: new AuthError(error) }
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// https://supabase.com/docs/reference/javascript/auth-exchangecodeforsession
|
|
1170
|
-
async exchangeCodeForSession() {
|
|
1171
|
-
//
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
/**
|
|
1175
|
-
* 删除当前用户
|
|
1176
|
-
* @param params
|
|
1177
|
-
* @returns
|
|
1178
|
-
*/
|
|
1179
|
-
async deleteUser(params: DeleteMeReq): Promise<CommonRes> {
|
|
1180
|
-
try {
|
|
1181
|
-
this.validateParams(params, {
|
|
1182
|
-
password: { required: true, message: 'Password is required' },
|
|
1183
|
-
})
|
|
1184
|
-
|
|
1185
|
-
const { sudo_token } = await this.oauthInstance.authApi.sudo(params)
|
|
1186
|
-
|
|
1187
|
-
await this.oauthInstance.authApi.deleteMe({ sudo_token })
|
|
1188
|
-
|
|
1189
|
-
return { data: {}, error: null }
|
|
1190
|
-
} catch (error) {
|
|
1191
|
-
return { data: {}, error: new AuthError(error) }
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
/**
|
|
1196
|
-
* 跳转系统默认登录页
|
|
1197
|
-
* @returns {Promise<authModels.ToDefaultLoginPage>}
|
|
1198
|
-
* @memberof Auth
|
|
1199
|
-
*/
|
|
1200
|
-
async toDefaultLoginPage(params: authModels.ToDefaultLoginPage = {}): Promise<CommonRes> {
|
|
1201
|
-
try {
|
|
1202
|
-
const configVersion = params.config_version || 'env'
|
|
1203
|
-
const query = Object.keys(params.query || {})
|
|
1204
|
-
.map(key => `${key}=${params.query[key]}`)
|
|
1205
|
-
.join('&')
|
|
1206
|
-
|
|
1207
|
-
if (adapterForWxMp.isMatch()) {
|
|
1208
|
-
wx.navigateTo({ url: `/packages/$wd_system/pages/login/index${query ? `?${query}` : ''}` })
|
|
1209
|
-
} else {
|
|
1210
|
-
const redirectUri = params.redirect_uri || window.location.href
|
|
1211
|
-
const urlObj = new URL(redirectUri)
|
|
1212
|
-
const loginPage = `${urlObj.origin}/__auth/?app_id=${params.app_id || ''}&env_id=${this.config.env}&client_id=${
|
|
1213
|
-
this.config.clientId || this.config.env
|
|
1214
|
-
}&config_version=${configVersion}&redirect_uri=${encodeURIComponent(redirectUri)}${query ? `&${query}` : ''}`
|
|
1215
|
-
window.location.href = loginPage
|
|
1216
|
-
}
|
|
1217
|
-
return { data: {}, error: null }
|
|
1218
|
-
} catch (error) {
|
|
1219
|
-
return { data: {}, error: new AuthError(error) }
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
/**
|
|
1224
|
-
* 自定义登录
|
|
1225
|
-
* @param getTickFn () => Promise<string>, 获取自定义登录 ticket 的函数
|
|
1226
|
-
* @returns
|
|
1227
|
-
*/
|
|
1228
|
-
async signInWithCustomTicket(getTickFn?: authModels.GetCustomSignTicketFn): Promise<SignInRes> {
|
|
1229
|
-
if (getTickFn) {
|
|
1230
|
-
this.setCustomSignFunc(getTickFn)
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
try {
|
|
1234
|
-
await this.oauthInstance.authApi.signInWithCustomTicket()
|
|
1235
|
-
const loginState = await this.createLoginState()
|
|
1236
|
-
|
|
1237
|
-
const { data: { session } = {} } = await this.getSession()
|
|
1238
|
-
|
|
1239
|
-
// loginState返回是为了兼容v2版本
|
|
1240
|
-
return { ...(loginState as any), data: { user: session.user, session }, error: null }
|
|
1241
|
-
} catch (error) {
|
|
1242
|
-
return { data: {}, error: new AuthError(error) }
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
/**
|
|
1247
|
-
* 小程序openId静默登录
|
|
1248
|
-
* @param params
|
|
1249
|
-
* @returns Promise<SignInRes>
|
|
1250
|
-
*/
|
|
1251
|
-
async signInWithOpenId({ useWxCloud = true } = {}): Promise<SignInRes> {
|
|
1252
|
-
if (!adapterForWxMp.isMatch()) {
|
|
1253
|
-
throw Error('wx api undefined')
|
|
1254
|
-
}
|
|
1255
|
-
const wxInfo = wx.getAccountInfoSync().miniProgram
|
|
1256
|
-
|
|
1257
|
-
const mainFunc = async (code) => {
|
|
1258
|
-
let result: authModels.GrantProviderTokenResponse | undefined = undefined
|
|
1259
|
-
let credentials: Credentials | undefined = undefined
|
|
1260
|
-
|
|
1261
|
-
try {
|
|
1262
|
-
result = await this.oauthInstance.authApi.grantProviderToken(
|
|
1263
|
-
{
|
|
1264
|
-
provider_id: wxInfo?.appId,
|
|
1265
|
-
provider_code: code,
|
|
1266
|
-
provider_params: {
|
|
1267
|
-
provider_code_type: 'open_id',
|
|
1268
|
-
appid: wxInfo?.appId,
|
|
1269
|
-
},
|
|
1270
|
-
},
|
|
1271
|
-
useWxCloud,
|
|
1272
|
-
)
|
|
1273
|
-
|
|
1274
|
-
if ((result as any)?.error_code || !result.provider_token) {
|
|
1275
|
-
throw result
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
credentials = await this.oauthInstance.authApi.signInWithProvider(
|
|
1279
|
-
{ provider_token: result.provider_token },
|
|
1280
|
-
useWxCloud,
|
|
1281
|
-
)
|
|
1282
|
-
|
|
1283
|
-
if ((credentials as any)?.error_code) {
|
|
1284
|
-
throw credentials
|
|
1285
|
-
}
|
|
1286
|
-
} catch (error) {
|
|
1287
|
-
throw error
|
|
1288
|
-
}
|
|
1289
|
-
await this.oauthInstance.oauth2client.setCredentials(credentials as Credentials)
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
try {
|
|
1293
|
-
await new Promise((resolve, reject) => {
|
|
1294
|
-
wx.login({
|
|
1295
|
-
success: async (res: { code: string }) => {
|
|
1296
|
-
try {
|
|
1297
|
-
await mainFunc(res.code)
|
|
1298
|
-
resolve(true)
|
|
1299
|
-
} catch (error) {
|
|
1300
|
-
reject(error)
|
|
1301
|
-
}
|
|
1302
|
-
},
|
|
1303
|
-
fail: (res: any) => {
|
|
1304
|
-
const error = new Error(res?.errMsg)
|
|
1305
|
-
;(error as any).code = res?.errno
|
|
1306
|
-
reject(error)
|
|
1307
|
-
},
|
|
1308
|
-
})
|
|
1309
|
-
})
|
|
1310
|
-
|
|
1311
|
-
const loginState = await this.createLoginState()
|
|
1312
|
-
|
|
1313
|
-
const { data: { session } = {} } = await this.getSession()
|
|
1314
|
-
|
|
1315
|
-
// loginState返回是为了兼容v2版本
|
|
1316
|
-
return { ...(loginState as any), data: { user: session.user, session }, error: null }
|
|
1317
|
-
} catch (error) {
|
|
1318
|
-
return { data: {}, error: new AuthError(error) }
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
/**
|
|
1323
|
-
* 小程序手机号授权登录,目前只支持全托管手机号授权登录
|
|
1324
|
-
* @param params
|
|
1325
|
-
* @returns Promise<SignInRes>
|
|
1326
|
-
*/
|
|
1327
|
-
async signInWithPhoneAuth({ phoneCode = '' }): Promise<SignInRes> {
|
|
1328
|
-
if (!adapterForWxMp.isMatch()) {
|
|
1329
|
-
return { data: {}, error: new AuthError({ message: 'wx api undefined' }) }
|
|
1330
|
-
}
|
|
1331
|
-
const wxInfo = wx.getAccountInfoSync().miniProgram
|
|
1332
|
-
const providerInfo = {
|
|
1333
|
-
provider_params: { provider_code_type: 'phone' },
|
|
1334
|
-
provider_id: wxInfo.appId,
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
const { code } = await wx.login()
|
|
1338
|
-
;(providerInfo as any).provider_code = code
|
|
1339
|
-
|
|
1340
|
-
try {
|
|
1341
|
-
let providerToken = await this.oauthInstance.authApi.grantProviderToken(providerInfo)
|
|
1342
|
-
if (providerToken.error_code) {
|
|
1343
|
-
throw providerToken
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
providerToken = await this.oauthInstance.authApi.patchProviderToken({
|
|
1347
|
-
provider_token: providerToken.provider_token,
|
|
1348
|
-
provider_id: wxInfo.appId,
|
|
1349
|
-
provider_params: {
|
|
1350
|
-
code: phoneCode,
|
|
1351
|
-
provider_code_type: 'phone',
|
|
1352
|
-
},
|
|
1353
|
-
})
|
|
1354
|
-
if (providerToken.error_code) {
|
|
1355
|
-
throw providerToken
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
const signInRes = await this.oauthInstance.authApi.signInWithProvider({
|
|
1359
|
-
provider_token: providerToken.provider_token,
|
|
1360
|
-
})
|
|
1361
|
-
|
|
1362
|
-
if ((signInRes as any)?.error_code) {
|
|
1363
|
-
throw signInRes
|
|
1364
|
-
}
|
|
1365
|
-
} catch (error) {
|
|
1366
|
-
return { data: {}, error: new AuthError(error) }
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
const loginState = await this.createLoginState()
|
|
1370
|
-
|
|
1371
|
-
const { data: { session } = {} } = await this.getSession()
|
|
1372
|
-
|
|
1373
|
-
// loginState返回是为了兼容v2版本
|
|
1374
|
-
return { ...(loginState as any), data: { user: session.user, session }, error: null }
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
private formatPhone(phone: string) {
|
|
1378
|
-
if (!/\s+/.test(phone) && /^\+\d{1,3}\d+/.test(phone)) {
|
|
1379
|
-
return phone.replace(/^(\+\d{1,2})(\d+)$/, '$1 $2')
|
|
1380
|
-
}
|
|
1381
|
-
return /^\+\d{1,3}\s+/.test(phone) ? phone : `+86 ${phone}`
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
private notifyListeners(event, session, info): OnAuthStateChangeCallback {
|
|
1385
|
-
this.listeners.forEach((callbacks) => {
|
|
1386
|
-
callbacks.forEach((callback) => {
|
|
1387
|
-
try {
|
|
1388
|
-
callback(event, session, info)
|
|
1389
|
-
} catch (error) {
|
|
1390
|
-
console.error('Error in auth state change callback:', error)
|
|
1391
|
-
}
|
|
1392
|
-
})
|
|
1393
|
-
})
|
|
1394
|
-
return
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
private async init(): Promise<{ error: Error | null }> {
|
|
1398
|
-
try {
|
|
1399
|
-
const credentials: Credentials = await this.oauthInstance.oauth2client.localCredentials.getCredentials()
|
|
1400
|
-
if (credentials) {
|
|
1401
|
-
this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.INITIAL_SESSION })
|
|
1402
|
-
}
|
|
1403
|
-
} catch (error) {
|
|
1404
|
-
// Ignore errors when checking for existing credentials
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
return { error: null }
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
private setupListeners() {
|
|
1411
|
-
this.config.eventBus?.on(EVENTS.AUTH_STATE_CHANGED, async (params) => {
|
|
1412
|
-
const event = params?.data?.event
|
|
1413
|
-
const info = params?.data?.info
|
|
1414
|
-
const {
|
|
1415
|
-
data: { session },
|
|
1416
|
-
} = await this.getSession()
|
|
1417
|
-
|
|
1418
|
-
this.notifyListeners(event, session, info)
|
|
1419
|
-
})
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
private convertToUser(userInfo: authModels.UserInfo & Partial<User>) {
|
|
1423
|
-
if (!userInfo) return null
|
|
1424
|
-
|
|
1425
|
-
// 优先使用 userInfo 中的数据(V3 API)
|
|
1426
|
-
const email = userInfo?.email || userInfo?.username
|
|
1427
|
-
const phone = userInfo?.phone_number
|
|
1428
|
-
const userId = userInfo?.sub || userInfo?.uid || ''
|
|
1429
|
-
|
|
1430
|
-
return {
|
|
1431
|
-
id: userId,
|
|
1432
|
-
aud: 'authenticated',
|
|
1433
|
-
role: userInfo.groups,
|
|
1434
|
-
email: email || '',
|
|
1435
|
-
email_confirmed_at: userInfo?.email_verified ? userInfo.created_at : userInfo.created_at,
|
|
1436
|
-
phone,
|
|
1437
|
-
phone_confirmed_at: phone ? userInfo.created_at : undefined,
|
|
1438
|
-
confirmed_at: userInfo.created_at,
|
|
1439
|
-
last_sign_in_at: userInfo.last_sign_in_at,
|
|
1440
|
-
app_metadata: {
|
|
1441
|
-
provider: userInfo.loginType?.toLowerCase() || 'cloudbase',
|
|
1442
|
-
providers: [userInfo.loginType?.toLowerCase() || 'cloudbase'],
|
|
1443
|
-
},
|
|
1444
|
-
user_metadata: {
|
|
1445
|
-
// V3 API 用户信息
|
|
1446
|
-
name: userInfo?.name,
|
|
1447
|
-
picture: userInfo?.picture,
|
|
1448
|
-
username: userInfo?.username, // 用户名称,长度 5-24 位,支持字符中英文、数字、特殊字符(仅支持_-),不支持中文
|
|
1449
|
-
gender: userInfo?.gender,
|
|
1450
|
-
locale: userInfo?.locale,
|
|
1451
|
-
// V2 API 兼容(使用 any 避免类型错误)
|
|
1452
|
-
uid: userInfo.uid,
|
|
1453
|
-
nickName: userInfo.nickName,
|
|
1454
|
-
avatarUrl: userInfo.avatarUrl || userInfo.picture,
|
|
1455
|
-
location: userInfo.location,
|
|
1456
|
-
hasPassword: userInfo.hasPassword,
|
|
1457
|
-
},
|
|
1458
|
-
identities:
|
|
1459
|
-
userInfo?.providers?.map(p => ({
|
|
1460
|
-
id: p.id || '',
|
|
1461
|
-
identity_id: p.id || '',
|
|
1462
|
-
user_id: userId,
|
|
1463
|
-
identity_data: {
|
|
1464
|
-
provider_id: p.id,
|
|
1465
|
-
provider_user_id: p.provider_user_id,
|
|
1466
|
-
name: p.name,
|
|
1467
|
-
},
|
|
1468
|
-
provider: p.id || 'cloudbase',
|
|
1469
|
-
created_at: userInfo.created_at,
|
|
1470
|
-
updated_at: userInfo.updated_at,
|
|
1471
|
-
last_sign_in_at: userInfo.last_sign_in_at,
|
|
1472
|
-
})) || [],
|
|
1473
|
-
created_at: userInfo.created_at,
|
|
1474
|
-
updated_at: userInfo.updated_at,
|
|
1475
|
-
is_anonymous: userInfo.name === 'anonymous',
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
/**
|
|
1480
|
-
* 参数校验辅助方法
|
|
1481
|
-
*/
|
|
1482
|
-
private validateParams(params: any, rules: { [key: string]: { required?: boolean; message: string } }): void {
|
|
1483
|
-
for (const [key, rule] of Object.entries(rules)) {
|
|
1484
|
-
if (rule.required && (params?.[key] === undefined || params?.[key] === null || params?.[key] === '')) {
|
|
1485
|
-
throw new AuthError({ message: rule.message })
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
/**
|
|
1491
|
-
* 校验必填参数组(至少有一个参数必须有值)
|
|
1492
|
-
*/
|
|
1493
|
-
private validateAtLeastOne(params: any, fieldGroups: string[][], message: string): void {
|
|
1494
|
-
const hasValue = fieldGroups.some(group => group.some(field => params?.[field] !== undefined && params?.[field] !== null && params?.[field] !== ''),)
|
|
1495
|
-
|
|
1496
|
-
if (!hasValue) {
|
|
1497
|
-
throw new AuthError({ message })
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
}
|