@cloudbase/auth 3.1.9 → 3.1.11

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/src/index.ts CHANGED
@@ -397,1019 +397,827 @@ class Auth extends AuthV1Compat {
397
397
  this.setAccessKey()
398
398
  }
399
399
 
400
- /**
401
- * 绑定手机号
402
- * @param phoneNumber
403
- * @param phoneCode
404
- */
405
- @catchErrorsDecorator({
406
- title: '绑定手机号失败',
407
- messages: [
408
- '请确认以下各项:',
409
- ' 1 - 调用 auth().bindPhoneNumber() 的语法或参数是否正确',
410
- ' 2 - 当前环境是否开通了短信验证码登录',
411
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
412
- ],
413
- })
414
- public async bindPhoneNumber(params: authModels.BindPhoneRequest) {
415
- return this.oauthInstance.authApi.editContact(params)
416
- }
400
+ // ========== new auth api methods merged below ==========
417
401
 
418
402
  /**
419
- * 解绑三方绑定
420
- * @param loginType
403
+ * https://supabase.com/docs/reference/javascript/auth-signinanonymously
404
+ * Sign in a user anonymously.
405
+ * const { data, error } = await auth.signInAnonymously();
406
+ * @param params
407
+ * @returns Promise<SignInRes>
421
408
  */
422
- @catchErrorsDecorator({
423
- title: '解除三方绑定失败',
424
- messages: [
425
- '请确认以下各项:',
426
- ' 1 - 调用 auth().unbindProvider() 的语法或参数是否正确',
427
- ' 2 - 当前账户是否已经与此登录方式解绑',
428
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
429
- ],
430
- })
431
- public async unbindProvider(params: authModels.UnbindProviderRequest): Promise<void> {
432
- return this.oauthInstance.authApi.unbindProvider(params)
433
- }
409
+ async signInAnonymously(params: SignInAnonymouslyReq): Promise<SignInRes> {
410
+ try {
411
+ await this.oauthInstance.authApi.signInAnonymously(params)
412
+ const loginState = await this.createLoginState()
434
413
 
435
- /**
436
- * 更新邮箱地址
437
- * @param email
438
- * @param sudo_token
439
- * @param verification_token
440
- */
441
- @catchErrorsDecorator({
442
- title: '绑定邮箱地址失败',
443
- messages: [
444
- '请确认以下各项:',
445
- ' 1 - 调用 auth().bindEmail() 的语法或参数是否正确',
446
- ' 2 - 当前环境是否开通了邮箱密码登录',
447
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
448
- ],
449
- })
450
- public bindEmail(params: authModels.BindEmailRequest) {
451
- return this.oauthInstance.authApi.editContact(params)
452
- }
414
+ const { data: { session } = {} } = await this.getSession()
453
415
 
454
- /**
455
- * verify
456
- * @param {authModels.VerifyRequest} params
457
- * @returns {Promise<authModels.VerifyResponse>}
458
- * @memberof User
459
- */
460
- @catchErrorsDecorator({
461
- title: '验证码验证失败',
462
- messages: [
463
- '请确认以下各项:',
464
- ' 1 - 调用 auth().verify() 的语法或参数是否正确',
465
- ' 2 - 当前环境是否开通了手机验证码/邮箱登录',
466
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
467
- ],
468
- })
469
- public async verify(params: authModels.VerifyRequest): Promise<authModels.VerifyResponse> {
470
- return this.oauthInstance.authApi.verify(params)
416
+ // loginState返回是为了兼容v2版本
417
+ return { ...(loginState as any), data: { user: session.user, session }, error: null }
418
+ } catch (error) {
419
+ return { data: {}, error: new AuthError(error) }
420
+ }
471
421
  }
472
422
 
473
423
  /**
474
- * 获取验证码
475
- * @param {authModels.GetVerificationRequest} params
476
- * @returns {Promise<authModels.GetVerificationResponse>}
477
- * @memberof User
424
+ * https://supabase.com/docs/reference/javascript/auth-signup
425
+ * 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.
426
+ * @param params
427
+ * @returns Promise<SignUpRes>
478
428
  */
479
- @catchErrorsDecorator({
480
- title: '获取验证码失败',
481
- messages: [
482
- '请确认以下各项:',
483
- ' 1 - 调用 auth().getVerification() 的语法或参数是否正确',
484
- ' 2 - 当前环境是否开通了手机验证码/邮箱登录',
485
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
486
- ],
487
- })
488
- public async getVerification(
489
- params: authModels.GetVerificationRequest,
490
- options?: { withCaptcha: boolean },
491
- ): Promise<authModels.GetVerificationResponse> {
492
- if (params.phone_number) {
429
+ async signUp(params: authModels.SignUpRequest & { phone?: string }): Promise<SignUpRes> {
430
+ if (params.phone_number || params.verification_code || params.verification_token || params.provider_token) {
493
431
  params.phone_number = this.formatPhone(params.phone_number)
432
+ await this.oauthInstance.authApi.signUp(params)
433
+ return this.createLoginState() as any
494
434
  }
495
- return this.oauthInstance.authApi.getVerification(params, options)
496
- }
435
+ try {
436
+ // 参数校验:email或phone必填其一
437
+ this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
497
438
 
498
- /**
499
- * 获取当前登录的用户信息-同步
500
- */
501
- get currentUser() {
502
- if (this.cache.mode === 'async') {
503
- // async storage的平台调用此API提示
504
- printWarn(
505
- ERRORS.INVALID_OPERATION,
506
- 'current platform\'s storage is asynchronous, please use getCurrentUser instead',
507
- )
508
- return
509
- }
439
+ // 第一步:发送验证码并存储 verificationInfo
440
+ const verificationInfo = await this.getVerification(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) },)
510
441
 
511
- const loginState = this.hasLoginState()
442
+ return {
443
+ data: {
444
+ // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
445
+ verifyOtp: async ({ token, messageId = verificationInfo.verification_id }): Promise<SignInRes> => {
446
+ try {
447
+ // 第三步:待用户输入完验证码之后,验证短信验证码
448
+ const verificationTokenRes = await this.verify({
449
+ verification_id: messageId || verificationInfo.verification_id,
450
+ verification_code: token,
451
+ })
512
452
 
513
- if (loginState) {
514
- return loginState.user || null
453
+ // 第四步:注册并登录或直接登录
454
+ // 如果用户已经存在,直接登录
455
+ if (verificationInfo.is_user) {
456
+ await this.signIn({
457
+ username: params.email || this.formatPhone(params.phone),
458
+ verification_token: verificationTokenRes.verification_token,
459
+ })
460
+ } else {
461
+ // 如果用户不存在,注册用户
462
+ const data = JSON.parse(JSON.stringify(params))
463
+ delete data.email
464
+ delete data.phone
465
+
466
+ await this.oauthInstance.authApi.signUp({
467
+ ...data,
468
+ ...(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) }),
469
+ verification_token: verificationTokenRes.verification_token,
470
+ verification_code: token,
471
+ })
472
+ await this.createLoginState()
473
+ }
474
+
475
+ const { data: { session } = {} } = await this.getSession()
476
+
477
+ return { data: { user: session.user, session }, error: null }
478
+ } catch (error) {
479
+ return { data: {}, error: new AuthError(error) }
480
+ }
481
+ },
482
+ },
483
+ error: null,
484
+ }
485
+ } catch (error) {
486
+ return { data: {}, error: new AuthError(error) }
515
487
  }
516
- return null
517
488
  }
518
489
 
519
490
  /**
520
- * 获取当前登录的用户信息-异步
491
+ * https://supabase.com/docs/reference/javascript/auth-signout
492
+ * const result = await auth.signOut();
493
+ *
494
+ * @param params
521
495
  */
522
- @catchErrorsDecorator({
523
- title: '获取用户信息失败',
524
- messages: [
525
- '请确认以下各项:',
526
- ' 1 - 调用 auth().getCurrentUser() 的语法或参数是否正确',
527
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
528
- ],
529
- })
530
- public async getCurrentUser(): Promise<(authModels.UserInfo & Partial<User>) | null> {
531
- const loginState = await this.getLoginState()
532
- if (loginState) {
533
- const userInfo = loginState.user.getLocalUserInfo() as authModels.UserInfo
534
- await loginState.user.checkLocalInfoAsync()
535
- return { ...loginState.user, ...userInfo } as unknown as authModels.UserInfo & Partial<User>
496
+ async signOut(params?: authModels.SignoutRequest,): Promise<authModels.SignoutResponse & { data: Object; error: AuthError }> {
497
+ try {
498
+ const { userInfoKey } = this.cache.keys
499
+ const res = await this.oauthInstance.authApi.signOut(params)
500
+ await this.cache.removeStoreAsync(userInfoKey)
501
+ this.setAccessKey()
502
+
503
+ this.config.eventBus?.fire(EVENTS.LOGIN_STATE_CHANGED, { eventType: LOGIN_STATE_CHANGED_TYPE.SIGN_OUT })
504
+
505
+ this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_OUT })
506
+
507
+ // res返回是为了兼容v2版本
508
+ return { ...res, data: {}, error: null }
509
+ } catch (error) {
510
+ return { data: {}, error: new AuthError(error) }
536
511
  }
537
- return null
538
512
  }
539
513
 
540
- // /**
541
- // * 匿名登录
542
- // * @returns {Promise<LoginState>}
543
- // * @memberof Auth
544
- // */
545
- // @catchErrorsDecorator({
546
- // title: '匿名登录失败',
547
- // messages: [
548
- // '请确认以下各项:',
549
- // ' 1 - 当前环境是否开启了匿名登录',
550
- // ' 2 - 调用 auth().signInAnonymously() 的语法或参数是否正确',
551
- // `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
552
- // ],
553
- // })
554
- // public async signInAnonymously(data: {
555
- // provider_token?: string
556
- // } = {},): Promise<LoginState> {
557
- // await this.oauthInstance.authApi.signInAnonymously(data)
558
- // return this.createLoginState()
559
- // }
560
-
561
514
  /**
562
- * 匿名登录
563
- * @returns {Promise<LoginState>}
564
- * @memberof Auth
515
+ * https://supabase.com/docs/reference/javascript/auth-onauthstatechange
516
+ * Receive a notification every time an auth event happens.
517
+ * @param callback
518
+ * @returns Promise<{ data: { subscription: Subscription }, error: Error | null }>
565
519
  */
566
- @catchErrorsDecorator({
567
- title: '小程序匿名登录失败',
568
- messages: [
569
- '请确认以下各项:',
570
- ' 1 - 当前环境是否开启了匿名登录',
571
- ' 2 - 调用 auth().signInAnonymouslyInWx() 的语法或参数是否正确',
572
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
573
- ],
574
- })
575
- public async signInAnonymouslyInWx({
576
- useWxCloud,
577
- }: {
578
- useWxCloud?: boolean
579
- } = {}): Promise<LoginState> {
580
- if (!adapterForWxMp.isMatch()) {
581
- throw Error('wx api undefined')
520
+ onAuthStateChange(callback: OnAuthStateChangeCallback) {
521
+ if (!this.hasListenerSetUp) {
522
+ this.setupListeners()
523
+ this.hasListenerSetUp = true
582
524
  }
583
- const wxInfo = wx.getAccountInfoSync().miniProgram
584
525
 
585
- const mainFunc = async (code) => {
586
- let result: authModels.GrantProviderTokenResponse | undefined = undefined
587
- let credentials: Credentials | undefined = undefined
526
+ const id = Math.random().toString(36)
588
527
 
589
- try {
590
- result = await this.oauthInstance.authApi.grantProviderToken(
591
- {
592
- provider_id: wxInfo?.appId,
593
- provider_code: code,
594
- provider_params: {
595
- provider_code_type: 'open_id',
596
- appid: wxInfo?.appId,
597
- },
598
- },
599
- useWxCloud,
600
- )
601
-
602
- if ((result as any)?.error_code || !result.provider_token) {
603
- throw result
604
- }
528
+ if (!this.listeners.has(id)) {
529
+ this.listeners.set(id, new Set())
530
+ }
605
531
 
606
- credentials = await this.oauthInstance.authApi.signInAnonymously(
607
- { provider_token: result.provider_token },
608
- useWxCloud,
609
- )
532
+ this.listeners.get(id)!.add(callback)
610
533
 
611
- if ((credentials as any)?.error_code) {
612
- throw credentials
534
+ // 返回 Subscription 对象
535
+ const subscription = {
536
+ id,
537
+ callback,
538
+ unsubscribe: () => {
539
+ const callbacks = this.listeners.get(id)
540
+ if (callbacks) {
541
+ callbacks.delete(callback)
542
+ if (callbacks.size === 0) {
543
+ this.listeners.delete(id)
544
+ }
613
545
  }
614
- } catch (error) {
615
- throw error
616
- }
546
+ },
617
547
  }
618
548
 
619
- await new Promise((resolve, reject) => {
620
- wx.login({
621
- success: async (res: { code: string }) => {
622
- try {
623
- await mainFunc(res.code)
624
- resolve(true)
625
- } catch (error) {
626
- reject(error)
627
- }
628
- },
629
- fail: (res: any) => {
630
- const error = new Error(res?.errMsg)
631
- ;(error as any).code = res?.errno
632
- reject(error)
633
- },
634
- })
635
- })
636
-
637
- return this.createLoginState(undefined, { asyncRefreshUser: true })
549
+ return {
550
+ data: { subscription },
551
+ }
638
552
  }
639
553
 
640
554
  /**
641
- * 小程序绑定OpenID
642
- * @returns {Promise<LoginState>}
643
- * @memberof Auth
555
+ * https://supabase.com/docs/reference/javascript/auth-signinwithpassword
556
+ * Log in an existing user with an email and password or phone and password or username and password.
557
+ * @param params
558
+ * @returns Promise<SignInRes>
644
559
  */
645
- @catchErrorsDecorator({
646
- title: '小程序绑定OpenID失败',
647
- messages: [
648
- '请确认以下各项:',
649
- ' 1 - 当前环境是否开启了小程序openId静默登录',
650
- ' 2 - 调用 auth().bindOpenId() 的语法或参数是否正确',
651
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
652
- ],
653
- })
654
- public async bindOpenId(): Promise<void> {
655
- if (!adapterForWxMp.isMatch()) {
656
- throw Error('wx api undefined')
657
- }
658
- const wxInfo = wx.getAccountInfoSync().miniProgram
659
-
660
- const mainFunc = async (code) => {
661
- let result: authModels.GrantProviderTokenResponse | undefined = undefined
662
-
663
- try {
664
- result = await this.oauthInstance.authApi.grantProviderToken({
665
- provider_id: wxInfo?.appId,
666
- provider_code: code,
667
- provider_params: {
668
- provider_code_type: 'open_id',
669
- appid: wxInfo?.appId,
670
- },
671
- })
560
+ async signInWithPassword(params: SignInWithPasswordReq): Promise<SignInRes> {
561
+ try {
562
+ // 参数校验:username/email/phone三选一,password必填
563
+ this.validateAtLeastOne(
564
+ params,
565
+ [['username'], ['email'], ['phone']],
566
+ 'You must provide either username, email, or phone',
567
+ )
568
+ this.validateParams(params, {
569
+ password: { required: true, message: 'Password is required' },
570
+ })
672
571
 
673
- if ((result as any)?.error_code || !result.provider_token) {
674
- throw result
675
- }
572
+ await this.signIn({
573
+ username: params.username || params.email || this.formatPhone(params.phone),
574
+ password: params.password,
575
+ ...(params.is_encrypt ? { isEncrypt: true, version: 'v2' } : {}),
576
+ })
577
+ const { data: { session } = {} } = await this.getSession()
676
578
 
677
- await this.oauthInstance.authApi.bindWithProvider({ provider_token: result.provider_token })
678
- } catch (error) {
679
- throw error
579
+ return { data: { user: session.user, session }, error: null }
580
+ } catch (error) {
581
+ const authError = new AuthError(error)
582
+ // 优化错误提示:登录失败时提供更友好的排查指引
583
+ if (authError.message?.includes('密码不正确') || authError.message?.includes('password')) {
584
+ console.warn('[CloudBase Auth] 登录失败提示:\n'
585
+ + ' 1. 请确认用户名/邮箱/手机号是否正确\n'
586
+ + ' 2. 请确认密码是否正确\n'
587
+ + ' 3. 如果用户不存在,请先通过 auth.signUp() 注册用户,或在云开发控制台手动创建用户\n'
588
+ + ' 4. 确认当前环境已开启对应的登录方式(控制台 → 环境 → 登录授权)',)
680
589
  }
590
+ return { data: {}, error: authError }
681
591
  }
592
+ }
682
593
 
683
- await new Promise((resolve, reject) => {
684
- wx.login({
685
- success: async (res: { code: string }) => {
686
- try {
687
- await mainFunc(res.code)
688
- resolve(true)
689
- } catch (error) {
690
- reject(error)
691
- }
692
- },
693
- fail: (res: any) => {
694
- const error = new Error(res?.errMsg)
695
- ;(error as any).code = res?.errno
696
- reject(error)
697
- },
594
+ /**
595
+ * https://supabase.com/docs/reference/javascript/auth-signinwithidtoken
596
+ * 第三方平台登录。如果用户不存在,会根据云开发平台-登录方式中对应身份源的登录模式配置,判断是否自动注册
597
+ * @param params
598
+ * @returns Promise<SignInRes>
599
+ */
600
+ async signInWithIdToken(params: SignInWithIdTokenReq): Promise<SignInRes> {
601
+ try {
602
+ // 参数校验:token必填
603
+ this.validateParams(params, {
604
+ token: { required: true, message: 'Token is required' },
698
605
  })
699
- })
700
606
 
701
- return
607
+ await this.signInWithProvider({
608
+ provider_token: params.token,
609
+ })
610
+ const { data: { session } = {} } = await this.getSession()
611
+
612
+ return { data: { user: session.user, session }, error: null }
613
+ } catch (error) {
614
+ return { data: {}, error: new AuthError(error) }
615
+ }
702
616
  }
703
617
 
704
618
  /**
705
- * 小程序unionId静默登录
706
- * @returns {Promise<LoginState>}
707
- * @memberof Auth
619
+ * https://supabase.com/docs/reference/javascript/auth-signinwithotp
620
+ * Log in a user using a one-time password (OTP).
621
+ * @param params
622
+ * @returns Promise<SignInWithOtpRes>
708
623
  */
709
- @catchErrorsDecorator({
710
- title: '小程序unionId静默登录失败',
711
- messages: [
712
- '请确认以下各项:',
713
- ' 1 - 当前环境是否开启了小程序unionId静默登录',
714
- ' 2 - 调用 auth().signInWithUnionId() 的语法或参数是否正确',
715
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
716
- ],
717
- })
718
- public async signInWithUnionId(): Promise<LoginState> {
719
- if (!adapterForWxMp.isMatch()) {
720
- throw Error('wx api undefined')
624
+ async signInWithOtp(params: SignInWithOtpReq): Promise<SignInWithOtpRes> {
625
+ if (params.options?.shouldCreateUser === undefined || !!params.options?.shouldCreateUser) {
626
+ return this.signUp({
627
+ email: params.email,
628
+ phone: params.phone,
629
+ })
721
630
  }
722
- try {
723
- await new Promise((resolve, reject) => {
724
- const wxInfo = wx.getAccountInfoSync().miniProgram
725
- wx.login({
726
- success: async (res: { code: string }) => {
727
- const providerId = wxInfo?.appId
728
- try {
729
- const result = await this.oauthInstance.authApi.grantProviderToken({
730
- provider_code: res.code,
731
- provider_id: providerId,
732
- provider_params: {
733
- provider_code_type: 'union_id',
734
- appid: wxInfo?.appId,
735
- },
736
- })
737
-
738
- const { provider_token: providerToken } = result
739
631
 
740
- if (!providerToken) {
741
- reject(result)
742
- return
743
- }
632
+ try {
633
+ // 参数校验:email或phone必填其一
634
+ this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
744
635
 
745
- const signInRes = await this.oauthInstance.authApi.signInWithProvider({
746
- provider_id: providerId,
747
- provider_token: providerToken,
748
- })
636
+ // 第一步:发送验证码并存储 verificationInfo
637
+ const verificationInfo = await this.getVerification(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) },)
749
638
 
750
- if ((signInRes as any)?.error_code) {
751
- reject(signInRes)
752
- return
753
- }
754
- resolve(true)
755
- } catch (error) {
756
- reject(error)
757
- }
758
- },
759
- fail: (res: any) => {
760
- const error = new Error(res?.errMsg)
761
- ;(error as any).code = res?.errno
762
- reject(error)
763
- },
764
- })
765
- })
639
+ return {
640
+ data: {
641
+ user: null,
642
+ session: null,
643
+ // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
644
+ verifyOtp: async ({ token, messageId = verificationInfo.verification_id }): Promise<SignInRes> => this.verifyOtp({
645
+ email: params.email,
646
+ phone: params.phone,
647
+ token,
648
+ messageId,
649
+ }),
650
+ },
651
+ error: null,
652
+ }
766
653
  } catch (error) {
767
- throw error
654
+ return { data: {}, error: new AuthError(error) }
768
655
  }
769
-
770
- return this.createLoginState()
771
656
  }
772
657
 
773
658
  /**
774
- * 小程序短信验证码登陆
775
- * @returns {Promise<LoginState>}
776
- * @memberof Auth
659
+ * 校验第三方平台授权登录回调
660
+ * @param params
661
+ * @returns Promise<SignInRes>
777
662
  */
778
- @catchErrorsDecorator({
779
- title: '短信验证码登陆',
780
- messages: [
781
- '请确认以下各项:',
782
- ' 1 - 当前环境是否开启了小程序短信验证码登陆',
783
- ' 2 - 调用 auth().signInWithSms() 的语法或参数是否正确',
784
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
785
- ],
786
- })
787
- public async signInWithSms({
788
- verificationInfo = { verification_id: '', is_user: false },
789
- verificationCode = '',
790
- phoneNum = '',
791
- bindInfo = undefined,
792
- }): Promise<LoginState> {
663
+ async verifyOAuth(params?: VerifyOAuthReq): Promise<VerifyOAuthRes> {
664
+ const data: any = {}
793
665
  try {
794
- return this.signInWithUsername({
795
- verificationInfo,
796
- verificationCode,
797
- bindInfo,
798
- username: phoneNum,
799
- loginType: 'sms',
800
- })
801
- } catch (error) {
802
- throw error
803
- }
804
- }
666
+ // 回调至 provider_redirect_uri 地址(url query中携带 授权code,state等参数),此时检查 state 是否符合预期(如 自己设置的 wx_open)
667
+ const code = params?.code || utils.getQuery('code')
668
+ const state = params?.state || utils.getQuery('state')
805
669
 
806
- /**
807
- * 邮箱验证码登陆
808
- * @returns {Promise<LoginState>}
809
- * @memberof Auth
810
- */
811
- @catchErrorsDecorator({
812
- title: '邮箱验证码登陆',
813
- messages: [
814
- '请确认以下各项:',
815
- ' 1 - 当前环境是否开启了邮箱登陆',
816
- ' 2 - 调用 auth().signInWithEmail() 的语法或参数是否正确',
817
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
818
- ],
819
- })
820
- public async signInWithEmail({
821
- verificationInfo = { verification_id: '', is_user: false },
822
- verificationCode = '',
823
- bindInfo = undefined,
824
- email = '',
825
- }): Promise<LoginState> {
826
- try {
827
- return this.signInWithUsername({
828
- verificationInfo,
829
- verificationCode,
830
- bindInfo,
831
- username: email,
832
- loginType: 'email',
833
- })
834
- } catch (error) {
835
- throw error
836
- }
837
- }
670
+ // 参数校验:code和state必填
671
+ if (!code) {
672
+ return { data: {}, error: new AuthError({ message: 'Code is required' }) }
673
+ }
838
674
 
839
- /**
840
- * 设置获取自定义登录 ticket 函数
841
- * @param {authModels.GetCustomSignTicketFn} getTickFn
842
- * @memberof Auth
843
- */
844
- public setCustomSignFunc(getTickFn: authModels.GetCustomSignTicketFn): void {
845
- this.oauthInstance.authApi.setCustomSignFunc(getTickFn)
846
- }
675
+ if (!state) {
676
+ return { data: {}, error: new AuthError({ message: 'State is required' }) }
677
+ }
847
678
 
848
- /**
849
- *
850
- * @returns {Promise<LoginState>}
851
- * @memberof Auth
852
- */
853
- // @catchErrorsDecorator({
854
- // title: '自定义登录失败',
855
- // messages: [
856
- // '请确认以下各项:',
857
- // ' 1 - 当前环境是否开启了自定义登录',
858
- // ' 2 - 调用 auth().signInWithCustomTicket() 的语法或参数是否正确',
859
- // ' 3 - ticket 是否归属于当前环境',
860
- // ' 4 - 创建 ticket 的自定义登录私钥是否过期',
861
- // `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
862
- // ],
863
- // })
864
- // public async signInWithCustomTicket(): Promise<LoginState> {
865
- // await this.oauthInstance.authApi.signInWithCustomTicket()
866
- // return this.createLoginState()
867
- // }
679
+ const cacheData = getBrowserSession(state)
680
+ data.type = cacheData?.type
868
681
 
869
- /**
870
- *
871
- * @param {authModels.SignInRequest} params
872
- * @returns {Promise<LoginState>}
873
- * @memberof Auth
874
- */
875
- public async signIn(params: authModels.SignInRequest): Promise<LoginState> {
876
- await this.oauthInstance.authApi.signIn(params)
877
- return this.createLoginState(params)
878
- }
682
+ const provider = params?.provider || cacheData?.provider || utils.getQuery('provider')
879
683
 
880
- // /**
881
- // *
882
- // * @param {authModels.SignUpRequest} params
883
- // * @returns {Promise<LoginState>}
884
- // * @memberof Auth
885
- // */
886
- // @catchErrorsDecorator({
887
- // title: '注册失败',
888
- // messages: [
889
- // '请确认以下各项:',
890
- // ' 1 - 当前环境是否开启了指定登录方式',
891
- // ' 2 - 调用 auth().signUp() 的语法或参数是否正确',
892
- // `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
893
- // ],
894
- // })
895
- // public async signUp(params: authModels.SignUpRequest): Promise<any> {
896
- // await this.oauthInstance.authApi.signUp(params)
897
- // return this.createLoginState()
898
- // }
684
+ if (!provider) {
685
+ return { data, error: new AuthError({ message: 'Provider is required' }) }
686
+ }
899
687
 
900
- /**
901
- * 设置密码
902
- * @param {authModels.SetPasswordRequest} params
903
- * @returns {Promise<void>}
904
- * @memberof Auth
905
- */
906
- public async setPassword(params: authModels.SetPasswordRequest): Promise<void> {
907
- return this.oauthInstance.authApi.setPassword(params)
908
- }
688
+ // state符合预期,则获取该三方平台token
689
+ const { provider_token: token } = await this.grantProviderToken({
690
+ provider_id: provider,
691
+ provider_redirect_uri: location.origin + location.pathname, // 指定三方平台跳回的 url 地址
692
+ provider_code: code, // 第三方平台跳转回页面时,url param 中携带的 code 参数
693
+ })
909
694
 
910
- /**
911
- * 检测用户名是否已经占用
912
- * @param username
913
- */
914
- @catchErrorsDecorator({
915
- title: '获取用户是否被占用失败',
916
- messages: [
917
- '请确认以下各项:',
918
- ' 1 - 调用 auth().isUsernameRegistered() 的语法或参数是否正确',
919
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
920
- ],
921
- })
922
- public async isUsernameRegistered(username: string): Promise<boolean> {
923
- if (typeof username !== 'string') {
924
- throwError(ERRORS.INVALID_PARAMS, 'username must be a string')
925
- }
695
+ let res: VerifyOAuthRes
926
696
 
927
- const { exist } = await this.oauthInstance.authApi.checkIfUserExist({ username })
928
- return exist
929
- }
697
+ if (cacheData.type === OAUTH_TYPE.BIND_IDENTITY) {
698
+ res = await this.oauthInstance.authApi.toBindIdentity({ provider_token: token, provider, fireEvent: true })
699
+ } else {
700
+ // 通过 provider_token 仅登录或登录并注册(与云开发平台-登录方式-身份源登录模式配置有关)
701
+ res = await this.signInWithIdToken({
702
+ token,
703
+ })
704
+ res.data = { ...data, ...res.data }
705
+ }
930
706
 
931
- /**
932
- * 获取本地登录态-同步
933
- */
934
- public hasLoginState(): LoginState | null {
935
- if (this.cache.mode === 'async') {
936
- // async storage的平台调用此API提示
937
- printWarn(
938
- ERRORS.INVALID_OPERATION,
939
- 'current platform\'s storage is asynchronous, please use getLoginState instead',
707
+ const localSearch = new URLSearchParams(location?.search)
708
+ localSearch.delete('code')
709
+ localSearch.delete('state')
710
+ res.data.redirectUrl = addUrlSearch(
711
+ cacheData?.search === undefined ? `?${localSearch.toString()}` : cacheData?.search,
712
+ cacheData?.hash || location.hash,
940
713
  )
941
- return
942
- }
714
+ removeBrowserSession(state)
943
715
 
944
- const oauthLoginState = this.oauthInstance?.authApi.hasLoginStateSync()
945
- if (oauthLoginState) {
946
- const loginState = new LoginState({
947
- envId: this.config.env,
948
- cache: this.cache,
949
- oauthInstance: this.oauthInstance,
950
- })
951
- return loginState
716
+ return res
717
+ } catch (error) {
718
+ return { data, error: new AuthError(error) }
952
719
  }
953
- return null
954
720
  }
955
721
 
956
722
  /**
957
- * 获取本地登录态-异步
958
- * 此API为兼容异步storage的平台
723
+ * https://supabase.com/docs/reference/javascript/auth-signinwithoauth
724
+ * 生成第三方平台授权 Uri (如微信二维码扫码授权网页)
725
+ * @param params
726
+ * @returns Promise<SignInOAuthRes>
959
727
  */
960
- @catchErrorsDecorator({
961
- title: '获取本地登录态失败',
962
- messages: [
963
- '请确认以下各项:',
964
- ' 1 - 调用 auth().getLoginState() 的语法或参数是否正确',
965
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
966
- ],
967
- })
968
- public async getLoginState() {
969
- let oauthLoginState = null
970
-
728
+ async signInWithOAuth(params: SignInWithOAuthReq): Promise<SignInOAuthRes> {
971
729
  try {
972
- oauthLoginState = await this.oauthInstance.authApi.getLoginState()
973
- } catch (error) {
974
- return null
975
- }
976
-
977
- if (oauthLoginState) {
978
- const loginState = new LoginState({
979
- envId: this.config.env,
980
- cache: this.cache,
981
- oauthInstance: this.oauthInstance,
730
+ // 参数校验:provider必填
731
+ this.validateParams(params, {
732
+ provider: { required: true, message: 'Provider is required' },
982
733
  })
983
- return loginState
984
- }
985
734
 
986
- return null
987
- }
735
+ const href = params.options?.redirectTo || location.href
988
736
 
989
- /**
990
- * @deprecated 使用 getCurrentUser 代替,因为与node-sdk方法名冲突
991
- * @returns
992
- */
993
- @catchErrorsDecorator({
994
- title: '获取用户信息失败',
995
- messages: [
996
- '请确认以下各项:',
997
- ' 1 - 是否已登录',
998
- ' 2 - 调用 auth().getUserInfo() 的语法或参数是否正确',
999
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1000
- ],
1001
- })
1002
- public async getUserInfo(): Promise<(authModels.UserInfo & Partial<User>) | null> {
1003
- return this.getCurrentUser()
1004
- }
737
+ const urlObject = new URL(href)
1005
738
 
1006
- @catchErrorsDecorator({
1007
- title: '获取微搭插件用户信息失败',
1008
- messages: [
1009
- '请确认以下各项:',
1010
- ' 1 - 是否已登录',
1011
- ' 2 - 调用 auth().getWedaUserInfo() 的语法或参数是否正确',
1012
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1013
- ],
1014
- })
1015
- public async getWedaUserInfo(): Promise<any> {
1016
- return this.oauthInstance.authApi.getWedaUserInfo()
1017
- }
739
+ const provider_redirect_uri = urlObject.origin + urlObject.pathname
1018
740
 
1019
- public async updateUserBasicInfo(params: authModels.ModifyUserBasicInfoRequest) {
1020
- const loginState = await this.getLoginState()
1021
- if (loginState) {
1022
- await (loginState.user as User).updateUserBasicInfo(params)
1023
- }
1024
- return
1025
- }
741
+ const state = params.options?.state || `prd-${params.provider}-${Math.random().toString(36)
742
+ .slice(2)}`
1026
743
 
1027
- /**
1028
- * getAuthHeader 兼容处理
1029
- * 返回空对象
1030
- */
1031
- public getAuthHeader(): {} {
1032
- console.error('Auth.getAuthHeader API 已废弃')
1033
- return {}
1034
- }
744
+ const { uri } = await this.genProviderRedirectUri({
745
+ provider_id: params.provider,
746
+ provider_redirect_uri,
747
+ state,
748
+ })
1035
749
 
1036
- /**
1037
- * 为已有账户绑第三方账户
1038
- * @param {authModels.BindWithProviderRequest} params
1039
- * @returns {Promise<void>}
1040
- * @memberof Auth
1041
- */
1042
- @catchErrorsDecorator({
1043
- title: '绑定第三方登录方式失败',
1044
- messages: [
1045
- '请确认以下各项:',
1046
- ' 1 - 调用 auth().bindWithProvider() 的语法或参数是否正确',
1047
- ' 2 - 此账户是否已经绑定此第三方',
1048
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1049
- ],
1050
- })
1051
- public async bindWithProvider(params: authModels.BindWithProviderRequest): Promise<void> {
1052
- return this.oauthInstance.authApi.bindWithProvider(params)
1053
- }
750
+ // 对 URL 进行解码
751
+ const decodedUri = decodeURIComponent(uri)
1054
752
 
1055
- /**
1056
- * 查询用户
1057
- * @param {authModels.QueryUserProfileRequest} appended_params
1058
- * @returns {Promise<authModels.UserProfile>}
1059
- * @memberof Auth
1060
- */
1061
- public async queryUser(queryObj: authModels.QueryUserProfileRequest): Promise<authModels.QueryUserProfileResponse> {
1062
- return this.oauthInstance.authApi.queryUserProfile(queryObj)
1063
- }
753
+ // 合并额外的查询参数
754
+ let finalUri = decodedUri
1064
755
 
1065
- public async getAccessToken() {
1066
- const oauthAccessTokenRes = await this.oauthInstance.oauth2client.getAccessToken()
1067
- return {
1068
- accessToken: oauthAccessTokenRes,
1069
- env: this.config.env,
1070
- }
1071
- }
756
+ if (params.options?.queryParams) {
757
+ const url = new URL(decodedUri)
758
+ Object.entries(params.options.queryParams).forEach(([key, value]) => {
759
+ url.searchParams.set(key, value)
760
+ })
761
+ finalUri = url.toString()
762
+ }
1072
763
 
1073
- public async grantProviderToken(params: authModels.GrantProviderTokenRequest,): Promise<authModels.GrantProviderTokenResponse> {
1074
- return this.oauthInstance.authApi.grantProviderToken(params)
1075
- }
764
+ saveToBrowserSession(state, {
765
+ provider: params.provider,
766
+ search: urlObject.search,
767
+ hash: urlObject.hash,
768
+ type: params.options?.type || OAUTH_TYPE.SIGN_IN,
769
+ })
1076
770
 
1077
- public async patchProviderToken(params: authModels.PatchProviderTokenRequest,): Promise<authModels.PatchProviderTokenResponse> {
1078
- return this.oauthInstance.authApi.patchProviderToken(params)
1079
- }
771
+ if (isBrowser() && !params.options?.skipBrowserRedirect) {
772
+ window.location.assign(finalUri)
773
+ }
1080
774
 
1081
- public async signInWithProvider(params: authModels.SignInWithProviderRequest): Promise<LoginState> {
1082
- await this.oauthInstance.authApi.signInWithProvider(params)
1083
- return this.createLoginState(params)
775
+ return { data: { url: finalUri, provider: params.provider }, error: null }
776
+ } catch (error) {
777
+ return { data: {}, error: new AuthError(error) }
778
+ }
1084
779
  }
1085
780
 
1086
- public async signInWithWechat(params: any = {}) {
1087
- await this.oauthInstance.authApi.signInWithWechat(params)
1088
- return this.createLoginState(params)
781
+ // https://supabase.com/docs/reference/javascript/auth-getclaims
782
+ async getClaims(): Promise<GetClaimsRes> {
783
+ try {
784
+ const { accessToken } = await this.getAccessToken()
785
+ const parsedToken = weAppJwtDecodeAll(accessToken)
786
+ return { data: parsedToken, error: null }
787
+ } catch (error) {
788
+ return { data: {}, error: new AuthError(error) }
789
+ }
1089
790
  }
1090
791
 
1091
- public async grantToken(params: authModels.GrantTokenRequest): Promise<LoginState> {
1092
- await this.oauthInstance.authApi.grantToken(params)
1093
- return this.createLoginState()
1094
- }
792
+ /**
793
+ * https://supabase.com/docs/reference/javascript/auth-resetpasswordforemail
794
+ * 通过 email 或手机号重置密码
795
+ * @param emailOrPhone 邮箱或手机号
796
+ * @returns Promise<ResetPasswordForEmailRes>
797
+ */
798
+ async resetPasswordForEmail(
799
+ emailOrPhone: string,
800
+ options?: { redirectTo?: string },
801
+ ): Promise<ResetPasswordForEmailRes> {
802
+ try {
803
+ // 参数校验:emailOrPhone必填
804
+ this.validateParams(
805
+ { emailOrPhone },
806
+ {
807
+ emailOrPhone: { required: true, message: 'Email or phone is required' },
808
+ },
809
+ )
1095
810
 
1096
- public async genProviderRedirectUri(params: authModels.GenProviderRedirectUriRequest,): Promise<authModels.GenProviderRedirectUriResponse> {
1097
- return this.oauthInstance.authApi.genProviderRedirectUri(params)
1098
- }
811
+ const { redirectTo } = options || {}
1099
812
 
1100
- public async resetPassword(params: authModels.ResetPasswordRequest): Promise<void> {
1101
- return this.oauthInstance.authApi.resetPassword(params)
1102
- }
813
+ // 判断是邮箱还是手机号
814
+ const isEmail = emailOrPhone.includes('@')
815
+ let verificationParams: { email?: string; phone_number?: string }
1103
816
 
1104
- public async deviceAuthorize(params: authModels.DeviceAuthorizeRequest): Promise<authModels.DeviceAuthorizeResponse> {
1105
- return this.oauthInstance.authApi.deviceAuthorize(params)
1106
- }
817
+ if (isEmail) {
818
+ verificationParams = { email: emailOrPhone }
819
+ } else {
820
+ // 正规化手机号
821
+ const formattedPhone = this.formatPhone(emailOrPhone)
822
+ verificationParams = { phone_number: formattedPhone }
823
+ }
824
+
825
+ // 第一步:发送验证码并存储 verificationInfo
826
+ const verificationInfo = await this.getVerification(verificationParams)
1107
827
 
1108
- public async sudo(params: authModels.SudoRequest): Promise<authModels.SudoResponse> {
1109
- return this.oauthInstance.authApi.sudo(params)
1110
- }
828
+ return {
829
+ data: {
830
+ // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
831
+ updateUser: async (attributes: UpdateUserAttributes): Promise<SignInRes> => {
832
+ this.validateParams(attributes, {
833
+ nonce: { required: true, message: 'Nonce is required' },
834
+ password: { required: true, message: 'Password is required' },
835
+ })
836
+ try {
837
+ // 第三步:待用户输入完验证码之后,验证验证码
838
+ const verificationTokenRes = await this.verify({
839
+ verification_id: verificationInfo.verification_id,
840
+ verification_code: attributes.nonce,
841
+ })
1111
842
 
1112
- public async deleteMe(params: authModels.WithSudoRequest): Promise<authModels.UserProfile> {
1113
- return this.oauthInstance.authApi.deleteMe(params)
1114
- }
843
+ await this.oauthInstance.authApi.resetPassword({
844
+ email: isEmail ? emailOrPhone : undefined,
845
+ phone_number: !isEmail ? emailOrPhone : undefined,
846
+ new_password: attributes.password,
847
+ verification_token: verificationTokenRes.verification_token,
848
+ })
1115
849
 
1116
- public async getProviders(): Promise<authModels.ProvidersResponse> {
1117
- return this.oauthInstance.authApi.getProviders()
1118
- }
850
+ this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, {
851
+ event: AUTH_STATE_CHANGED_TYPE.PASSWORD_RECOVERY,
852
+ })
1119
853
 
1120
- public async loginScope(): Promise<string> {
1121
- return this.oauthInstance.authApi.loginScope()
1122
- }
854
+ const res = await this.signInWithPassword({
855
+ email: isEmail ? emailOrPhone : undefined,
856
+ phone: !isEmail ? emailOrPhone : undefined,
857
+ password: attributes.password,
858
+ })
1123
859
 
1124
- public async loginGroups(): Promise<string[]> {
1125
- return this.oauthInstance.authApi.loginGroups()
1126
- }
860
+ if (redirectTo && isBrowser()) {
861
+ window.location.assign(redirectTo)
862
+ }
1127
863
 
1128
- public async onLoginStateChanged(callback: Function) {
1129
- this.config.eventBus?.on(EVENTS.LOGIN_STATE_CHANGED, async (params) => {
1130
- // getLoginState会重复触发getCredentials,导致死循环,所以getCredentials出错不再出发getLoginState
1131
- const loginState = params?.data?.eventType !== LOGIN_STATE_CHANGED_TYPE.CREDENTIALS_ERROR ? await this.getLoginState() : {}
1132
- callback.call(this, { ...params, ...loginState })
1133
- })
1134
- // 立刻执行一次回调
1135
- const loginState = await this.getLoginState()
1136
- callback.call(this, loginState)
864
+ return res
865
+ } catch (error) {
866
+ return { data: {}, error: new AuthError(error) }
867
+ }
868
+ },
869
+ },
870
+ error: null,
871
+ }
872
+ } catch (error) {
873
+ return { data: {}, error: new AuthError(error) }
874
+ }
1137
875
  }
1138
876
 
1139
- // v1 兼容 API 方法已移至 AuthV1Compat 基类 (v1-compat.ts)
1140
-
1141
877
  /**
1142
- * 强制刷新token
1143
- * @param params
878
+ * 通过旧密码重置密码
879
+ * @param new_password
880
+ * @param old_password
1144
881
  * @returns
1145
882
  */
1146
- public async refreshTokenForce(params: { version?: string }): Promise<Credentials> {
1147
- return this.oauthInstance.authApi.refreshTokenForce(params)
883
+ async resetPasswordForOld(params: ResetPasswordForOldReq) {
884
+ try {
885
+ await this.oauthInstance.authApi.updatePasswordByOld({
886
+ old_password: params.old_password,
887
+ new_password: params.new_password,
888
+ })
889
+
890
+ const { data: { session } = {} } = await this.getSession()
891
+
892
+ return { data: { user: session.user, session }, error: null }
893
+ } catch (error) {
894
+ return { data: {}, error: new AuthError(error) }
895
+ }
1148
896
  }
1149
897
 
1150
898
  /**
1151
- * 获取身份信息
1152
- * @returns
899
+ * https://supabase.com/docs/reference/javascript/auth-verifyotp
900
+ * Log in a user given a User supplied OTP and verificationId received through mobile or email.
901
+ * @param params
902
+ * @returns Promise<SignInRes>
1153
903
  */
1154
- public async getCredentials(): Promise<Credentials> {
1155
- return this.oauthInstance.authApi.getCredentials()
904
+ async verifyOtp(params: VerifyOtpReq): Promise<SignInRes> {
905
+ try {
906
+ const { type } = params
907
+ // 参数校验:token和verificationInfo必填
908
+ this.validateParams(params, {
909
+ token: { required: true, message: 'Token is required' },
910
+ messageId: { required: true, message: 'messageId is required' },
911
+ })
912
+
913
+ if (['phone_change', 'email_change'].includes(type)) {
914
+ await this.verify({
915
+ verification_id: params.messageId,
916
+ verification_code: params.token,
917
+ })
918
+ } else {
919
+ await this.signInWithUsername({
920
+ verificationInfo: { verification_id: params.messageId, is_user: true },
921
+ verificationCode: params.token,
922
+ username: params.email || this.formatPhone(params.phone) || '',
923
+ loginType: params.email ? 'email' : 'phone',
924
+ })
925
+ }
926
+
927
+ const { data: { session } = {} } = await this.getSession()
928
+
929
+ return { data: { user: session.user, session }, error: null }
930
+ } catch (error) {
931
+ return { data: {}, error: new AuthError(error) }
932
+ }
1156
933
  }
934
+
1157
935
  /**
1158
- * 写入身份信息
936
+ * https://supabase.com/docs/reference/javascript/auth-getSession
937
+ * Returns the session, refreshing it if necessary.
938
+ * @returns Promise<SignInRes>
1159
939
  */
1160
- public async setCredentials(credentials: Credentials) {
1161
- await this.oauthInstance.oauth2client.setCredentials(credentials)
1162
- }
940
+ async getSession(): Promise<SignInRes> {
941
+ try {
942
+ const credentials: Credentials = await this.oauthInstance.oauth2client.getCredentials()
1163
943
 
1164
- @catchErrorsDecorator({
1165
- title: '获取身份源类型',
1166
- messages: [
1167
- '请确认以下各项:',
1168
- ' 1 - 调用 auth().getProviderSubType() 的语法或参数是否正确',
1169
- `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1170
- ],
1171
- })
1172
- public async getProviderSubType(): Promise<authModels.ProviderSubType> {
1173
- return this.oauthInstance.authApi.getProviderSubType()
1174
- }
944
+ if (!credentials || credentials.scope === 'accessKey') {
945
+ return { data: { session: null }, error: null }
946
+ }
1175
947
 
1176
- public async createCaptchaData(params: { state: string; redirect_uri?: string }) {
1177
- return this.oauthInstance.authApi.createCaptchaData(params)
1178
- }
948
+ const { data: { user } = {} } = await this.getUser()
1179
949
 
1180
- public async verifyCaptchaData(params: { token: string; key: string }) {
1181
- return this.oauthInstance.authApi.verifyCaptchaData(params)
950
+ return { data: { session: { ...credentials, user }, user }, error: null }
951
+ } catch (error) {
952
+ return { data: {}, error: new AuthError(error) }
953
+ }
1182
954
  }
1183
955
 
1184
- public async getMiniProgramQrCode(params: authModels.GetMiniProgramQrCodeRequest,): Promise<authModels.GetMiniProgramQrCodeResponse> {
1185
- return this.oauthInstance.authApi.getMiniProgramCode(params)
1186
- }
956
+ /**
957
+ * https://supabase.com/docs/reference/javascript/auth-refreshsession
958
+ * 无论过期状态如何,都返回一个新的会话
959
+ * @param refresh_token
960
+ * @returns Promise<SignInRes>
961
+ */
962
+ async refreshSession(refresh_token?: string): Promise<SignInRes> {
963
+ try {
964
+ const credentials: Credentials = await this.oauthInstance.oauth2client.localCredentials.getCredentials()
965
+ credentials.refresh_token = refresh_token || credentials.refresh_token
966
+ const newTokens = await this.oauthInstance.oauth2client.refreshToken(credentials)
967
+ const { data: { user } = {} } = await this.getUser()
1187
968
 
1188
- public async getMiniProgramQrCodeStatus(params: authModels.GetMiniProgramQrCodeStatusRequest,): Promise<authModels.GetMiniProgramQrCodeStatusResponse> {
1189
- return this.oauthInstance.authApi.getMiniProgramQrCodeStatus(params)
969
+ return { data: { user, session: { ...newTokens, user } }, error: null }
970
+ } catch (error) {
971
+ return { data: {}, error: new AuthError(error) }
972
+ }
1190
973
  }
1191
974
 
1192
- public async modifyPassword(params: authModels.ModifyUserBasicInfoRequest): Promise<void> {
1193
- return this.oauthInstance.authApi.modifyPassword(params)
975
+ /**
976
+ * https://supabase.com/docs/reference/javascript/auth-getuser
977
+ * 如果存在现有会话,则获取当前用户详细信息
978
+ * @returns Promise<GetUserRes>
979
+ */
980
+ async getUser(): Promise<GetUserRes> {
981
+ try {
982
+ const user = this.convertToUser(await this.getCurrentUser())
983
+ return { data: { user }, error: null }
984
+ } catch (error) {
985
+ return { data: {}, error: new AuthError(error) }
986
+ }
1194
987
  }
1195
988
 
1196
- public async modifyPasswordWithoutLogin(params: authModels.ModifyPasswordWithoutLoginRequest): Promise<void> {
1197
- return this.oauthInstance.authApi.modifyPasswordWithoutLogin(params)
1198
- }
989
+ /**
990
+ * 刷新用户信息
991
+ * @returns Promise<CommonRes>
992
+ */
993
+ async refreshUser(): Promise<CommonRes> {
994
+ try {
995
+ await this.currentUser.refresh()
1199
996
 
1200
- public async getUserBehaviorLog(params: authModels.GetUserBehaviorLog): Promise<authModels.GetUserBehaviorLogRes> {
1201
- return this.oauthInstance.authApi.getUserBehaviorLog(params)
997
+ const { data: { session } = {} } = await this.getSession()
998
+ return { data: { user: session.user, session }, error: null }
999
+ } catch (error) {
1000
+ return { data: {}, error: new AuthError(error) }
1001
+ }
1202
1002
  }
1203
1003
 
1204
1004
  /**
1205
- * sms/email 验证码登录/注册,逻辑一致收敛
1005
+ * https://supabase.com/docs/reference/javascript/auth-updateuser
1006
+ * 更新用户信息
1007
+ * @param params
1008
+ * @returns Promise<GetUserRes | UpdateUserWithVerificationRes>
1206
1009
  */
1207
- public async signInWithUsername({
1208
- verificationInfo = { verification_id: '', is_user: false },
1209
- verificationCode = '',
1210
- username: rawUsername = '',
1211
- bindInfo = undefined,
1212
- loginType = '',
1213
- }: {
1214
- verificationInfo?: authModels.GetVerificationResponse
1215
- verificationCode?: string
1216
- username?: string
1217
- bindInfo?: any
1218
- loginType?: string
1219
- }): Promise<LoginState> {
1010
+ async updateUser(params: UpdateUserReq): Promise<GetUserRes | UpdateUserWithVerificationRes> {
1220
1011
  try {
1221
- // 1. 验证验证码
1222
- const verifyRes = await this.oauthInstance.authApi.verify({
1223
- verification_id: verificationInfo.verification_id,
1224
- verification_code: verificationCode,
1225
- })
1226
-
1227
- if ((verifyRes as any)?.error_code) {
1228
- throw verifyRes
1012
+ // 参数校验:至少有一个更新字段被提供
1013
+ const hasValue = Object.keys(params).some(key => params[key] !== undefined && params[key] !== null && params[key] !== '',)
1014
+ if (!hasValue) {
1015
+ throw new AuthError({ message: 'At least one field must be provided for update' })
1229
1016
  }
1230
1017
 
1231
- // eslint-disable-next-line @typescript-eslint/naming-convention
1232
- const { verification_token } = verifyRes
1018
+ const { email, phone, ...restParams } = params
1233
1019
 
1234
- // 手机登录参数
1235
- let username = this.formatPhone(rawUsername)
1236
- let signUpParam: any = { phone_number: username }
1020
+ // 检查是否需要更新 email 或 phone
1021
+ const needsEmailVerification = email !== undefined
1022
+ const needsPhoneVerification = phone !== undefined
1237
1023
 
1238
- // 邮箱登录参数
1239
- if (loginType === 'email') {
1240
- username = rawUsername
1241
- signUpParam = { email: username }
1242
- }
1024
+ let extraRes = {}
1243
1025
 
1244
- // 2. 根据是否已经是用户,分别走登录或注册逻辑
1245
- if (verificationInfo.is_user) {
1246
- // 私有化环境或者自定义应用走v1版本的老逻辑
1247
- const signInRes = await this.oauthInstance.authApi.signIn({
1248
- username,
1249
- verification_token,
1250
- })
1026
+ if (needsEmailVerification || needsPhoneVerification) {
1027
+ // 需要发送验证码
1028
+ let verificationParams: { email?: string; phone_number?: string }
1029
+ let verificationType: 'email_change' | 'phone_change'
1251
1030
 
1252
- if ((signInRes as any)?.error_code) {
1253
- throw signInRes
1031
+ if (needsEmailVerification) {
1032
+ verificationParams = { email: params.email }
1033
+ verificationType = 'email_change'
1034
+ } else {
1035
+ // 正规化手机号
1036
+ const formattedPhone = this.formatPhone(params.phone)
1037
+ verificationParams = { phone_number: formattedPhone }
1038
+ verificationType = 'phone_change'
1254
1039
  }
1255
1040
 
1256
- if (bindInfo) {
1257
- const bindRes = await this.oauthInstance.authApi.bindWithProvider({
1258
- provider_token: (bindInfo as any)?.providerToken,
1259
- })
1041
+ // 发送验证码
1042
+ const verificationInfo = await this.getVerification(verificationParams)
1260
1043
 
1261
- if ((bindRes as any)?.error_code) {
1262
- throw bindRes
1263
- }
1264
- }
1265
- } else {
1266
- // 自定义应用走signUp逻辑
1267
- const signUpRes = await this.oauthInstance.authApi.signUp({
1268
- ...signUpParam,
1269
- verification_token,
1270
- provider_token: (bindInfo as any)?.providerId,
1271
- })
1044
+ Object.keys(restParams).length > 0 && (await this.updateUserBasicInfo(restParams))
1272
1045
 
1273
- if ((signUpRes as any)?.error_code) {
1274
- throw signUpRes
1046
+ extraRes = {
1047
+ messageId: verificationInfo.verification_id,
1048
+ verifyOtp: async (verifyParams: { email?: string; phone?: string; token: string }): Promise<GetUserRes> => {
1049
+ try {
1050
+ if (verifyParams.email && params.email === verifyParams.email) {
1051
+ // 验证码验证
1052
+ await this.verifyOtp({
1053
+ type: 'email_change',
1054
+ email: params.email,
1055
+ token: verifyParams.token,
1056
+ messageId: verificationInfo.verification_id,
1057
+ })
1058
+ await this.updateUserBasicInfo({ email: params.email })
1059
+ } else if (verifyParams.phone && params.phone === verifyParams.phone) {
1060
+ // 验证码验证
1061
+ await this.verifyOtp({
1062
+ type: 'phone_change',
1063
+ phone: params.phone,
1064
+ token: verifyParams.token,
1065
+ messageId: verificationInfo.verification_id,
1066
+ })
1067
+ await this.updateUserBasicInfo({ phone: this.formatPhone(params.phone) })
1068
+ } else {
1069
+ await this.verifyOtp({
1070
+ type: verificationType,
1071
+ email: needsEmailVerification ? params.email : undefined,
1072
+ phone: !needsEmailVerification ? params.phone : undefined,
1073
+ token: verifyParams.token,
1074
+ messageId: verificationInfo.verification_id,
1075
+ })
1076
+ // 验证成功后更新用户信息
1077
+ await this.updateUserBasicInfo(params)
1078
+ }
1079
+
1080
+ const {
1081
+ data: { user },
1082
+ } = await this.getUser()
1083
+ this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.USER_UPDATED })
1084
+
1085
+ return { data: { user }, error: null }
1086
+ } catch (error) {
1087
+ return { data: {}, error: new AuthError(error) }
1088
+ }
1089
+ },
1275
1090
  }
1091
+ } else {
1092
+ // 不需要验证,直接更新
1093
+ await this.updateUserBasicInfo(params)
1276
1094
  }
1095
+ const {
1096
+ data: { user },
1097
+ } = await this.getUser()
1098
+ this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.USER_UPDATED })
1277
1099
 
1278
- return this.createLoginState()
1100
+ return { data: { user, ...extraRes }, error: null }
1279
1101
  } catch (error) {
1280
- throw error
1102
+ return { data: {}, error: new AuthError(error) }
1281
1103
  }
1282
1104
  }
1283
1105
 
1284
- async createLoginState(
1285
- params?: { version?: string; query?: any },
1286
- options?: { asyncRefreshUser?: boolean; userInfo?: any },
1287
- ): Promise<LoginState> {
1288
- const loginState = new LoginState({
1289
- envId: this.config.env,
1290
- cache: this.cache,
1291
- oauthInstance: this.oauthInstance,
1292
- })
1293
-
1294
- await loginState.checkLocalStateAsync()
1106
+ /**
1107
+ * https://supabase.com/docs/reference/javascript/auth-getuseridentities
1108
+ * 获取所有身份源
1109
+ * @returns Promise<GetUserIdentitiesRes>
1110
+ */
1111
+ async getUserIdentities(): Promise<GetUserIdentitiesRes> {
1112
+ try {
1113
+ const providers = await this.oauthInstance.authApi.getProviders()
1295
1114
 
1296
- if (options?.userInfo) {
1297
- loginState.user.setLocalUserInfo(options.userInfo)
1298
- } else {
1299
- if (options?.asyncRefreshUser) {
1300
- loginState.user.refresh(params)
1301
- } else {
1302
- await loginState.user.refresh(params)
1115
+ return {
1116
+ data: {
1117
+ identities: providers?.data?.filter(v => !!v.bind) as unknown as GetUserIdentitiesRes['data']['identities'],
1118
+ },
1119
+ error: null,
1303
1120
  }
1121
+ } catch (error) {
1122
+ return { data: {}, error: new AuthError(error) }
1304
1123
  }
1305
-
1306
- this.config.eventBus?.fire(EVENTS.LOGIN_STATE_CHANGED, { eventType: LOGIN_STATE_CHANGED_TYPE.SIGN_IN })
1307
-
1308
- this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_IN })
1309
- return loginState
1310
1124
  }
1311
1125
 
1312
- async setAccessKey() {
1313
- let accessToken = ''
1314
- let scope = ''
1315
- if (this.config.accessKey) {
1316
- accessToken = this.config.accessKey
1317
- scope = 'accessKey'
1318
- } else if (this.config.auth?.secretId && this.config.auth?.secretKey) {
1319
- accessToken = ''
1320
- scope = DEFAULT_NODE_ACCESS_SCOPE
1321
- }
1322
-
1323
- if (!scope) {
1324
- return
1325
- }
1326
-
1126
+ /**
1127
+ * https://supabase.com/docs/reference/javascript/auth-linkidentity
1128
+ * 绑定身份源到当前用户
1129
+ * @param params
1130
+ * @returns Promise<LinkIdentityRes>
1131
+ */
1132
+ async linkIdentity(params: LinkIdentityReq): Promise<LinkIdentityRes> {
1327
1133
  try {
1328
- this.oauthInstance.oauth2client.setAccessKeyCredentials({
1329
- access_token: accessToken,
1330
- token_type: 'Bearer',
1331
- scope,
1332
- expires_at: new Date(+new Date() + +new Date()),
1333
- expires_in: +new Date() + +new Date(),
1134
+ // 参数校验:provider必填
1135
+ this.validateParams(params, {
1136
+ provider: { required: true, message: 'Provider is required' },
1137
+ })
1138
+
1139
+ await this.signInWithOAuth({
1140
+ provider: params.provider,
1141
+ options: {
1142
+ type: OAUTH_TYPE.BIND_IDENTITY,
1143
+ },
1334
1144
  })
1145
+
1146
+ return { data: { provider: params.provider }, error: null }
1335
1147
  } catch (error) {
1336
- console.warn('accessKey error: ', error)
1148
+ return { data: {}, error: new AuthError(error) }
1337
1149
  }
1338
1150
  }
1339
1151
 
1340
- // ========== new auth api methods merged below ==========
1341
-
1342
1152
  /**
1343
- * https://supabase.com/docs/reference/javascript/auth-signinanonymously
1344
- * Sign in a user anonymously.
1345
- * const { data, error } = await auth.signInAnonymously();
1153
+ * https://supabase.com/docs/reference/javascript/auth-unlinkidentity
1154
+ * 解绑身份源
1346
1155
  * @param params
1347
- * @returns Promise<SignInRes>
1156
+ * @returns Promise<CommonRes>
1348
1157
  */
1349
- async signInAnonymously(params: SignInAnonymouslyReq): Promise<SignInRes> {
1158
+ async unlinkIdentity(params: UnlinkIdentityReq): Promise<CommonRes> {
1350
1159
  try {
1351
- await this.oauthInstance.authApi.signInAnonymously(params)
1352
- const loginState = await this.createLoginState()
1160
+ // 参数校验:provider必填
1161
+ this.validateParams(params, {
1162
+ provider: { required: true, message: 'Provider is required' },
1163
+ })
1353
1164
 
1354
- const { data: { session } = {} } = await this.getSession()
1165
+ await this.oauthInstance.authApi.unbindProvider({ provider_id: params.provider })
1355
1166
 
1356
- // loginState返回是为了兼容v2版本
1357
- return { ...(loginState as any), data: { user: session.user, session }, error: null }
1167
+ return { data: {}, error: null }
1358
1168
  } catch (error) {
1359
1169
  return { data: {}, error: new AuthError(error) }
1360
1170
  }
1361
1171
  }
1362
1172
 
1363
1173
  /**
1364
- * https://supabase.com/docs/reference/javascript/auth-signup
1365
- * 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.
1366
- * @param params
1367
- * @returns Promise<SignUpRes>
1174
+ * https://supabase.com/docs/reference/javascript/auth-reauthentication
1175
+ * 重新认证
1176
+ * @returns Promise<ReauthenticateRes>
1368
1177
  */
1369
- async signUp(params: authModels.SignUpRequest & { phone?: string }): Promise<SignUpRes> {
1370
- if (params.phone_number || params.verification_code || params.verification_token || params.provider_token) {
1371
- params.phone_number = this.formatPhone(params.phone_number)
1372
- await this.oauthInstance.authApi.signUp(params)
1373
- return this.createLoginState() as any
1374
- }
1178
+ async reauthenticate(): Promise<ReauthenticateRes> {
1375
1179
  try {
1376
- // 参数校验:email或phone必填其一
1377
- this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
1180
+ const {
1181
+ data: { user },
1182
+ } = await this.getUser()
1183
+
1184
+ this.validateAtLeastOne(user, [['email', 'phone']], 'You must provide either an email or phone number')
1185
+ const userInfo = user.email ? { email: user.email } : { phone_number: this.formatPhone(user.phone) }
1378
1186
 
1379
1187
  // 第一步:发送验证码并存储 verificationInfo
1380
- const verificationInfo = await this.getVerification(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) },)
1188
+ const verificationInfo = await this.getVerification(userInfo)
1381
1189
 
1382
1190
  return {
1383
1191
  data: {
1384
1192
  // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
1385
- verifyOtp: async ({ token, messageId = verificationInfo.verification_id }): Promise<SignInRes> => {
1193
+ updateUser: async (attributes: UpdateUserAttributes): Promise<SignInRes> => {
1194
+ this.validateParams(attributes, {
1195
+ nonce: { required: true, message: 'Nonce is required' },
1196
+ })
1386
1197
  try {
1387
- // 第三步:待用户输入完验证码之后,验证短信验证码
1388
- const verificationTokenRes = await this.verify({
1389
- verification_id: messageId || verificationInfo.verification_id,
1390
- verification_code: token,
1391
- })
1198
+ if (attributes.password) {
1199
+ // 第三步:待用户输入完验证码之后,验证验证码
1200
+ const verificationTokenRes = await this.verify({
1201
+ verification_id: verificationInfo.verification_id,
1202
+ verification_code: attributes.nonce,
1203
+ })
1392
1204
 
1393
- // 第四步:注册并登录或直接登录
1394
- // 如果用户已经存在,直接登录
1395
- if (verificationInfo.is_user) {
1396
- await this.signIn({
1397
- username: params.email || this.formatPhone(params.phone),
1205
+ // 第四步:获取 sudo_token
1206
+ const sudoRes = await this.oauthInstance.authApi.sudo({
1398
1207
  verification_token: verificationTokenRes.verification_token,
1399
1208
  })
1400
- } else {
1401
- // 如果用户不存在,注册用户
1402
- const data = JSON.parse(JSON.stringify(params))
1403
- delete data.email
1404
- delete data.phone
1405
1209
 
1406
- await this.oauthInstance.authApi.signUp({
1407
- ...data,
1408
- ...(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) }),
1409
- verification_token: verificationTokenRes.verification_token,
1410
- verification_code: token,
1210
+ await this.oauthInstance.authApi.setPassword({
1211
+ new_password: attributes.password,
1212
+ sudo_token: sudoRes.sudo_token,
1213
+ })
1214
+ } else {
1215
+ await this.signInWithUsername({
1216
+ verificationInfo,
1217
+ verificationCode: attributes.nonce,
1218
+ ...userInfo,
1219
+ loginType: userInfo.email ? 'email' : 'phone',
1411
1220
  })
1412
- await this.createLoginState()
1413
1221
  }
1414
1222
 
1415
1223
  const { data: { session } = {} } = await this.getSession()
@@ -1428,1056 +1236,1225 @@ class Auth extends AuthV1Compat {
1428
1236
  }
1429
1237
 
1430
1238
  /**
1431
- * https://supabase.com/docs/reference/javascript/auth-signout
1432
- * const result = await auth.signOut();
1433
- *
1239
+ * https://supabase.com/docs/reference/javascript/auth-resend
1240
+ * 重新发送验证码
1434
1241
  * @param params
1242
+ * @returns Promise<ResendRes>
1435
1243
  */
1436
- async signOut(params?: authModels.SignoutRequest,): Promise<authModels.SignoutResponse & { data: Object; error: AuthError }> {
1244
+ async resend(params: ResendReq): Promise<ResendRes> {
1437
1245
  try {
1438
- const { userInfoKey } = this.cache.keys
1439
- const res = await this.oauthInstance.authApi.signOut(params)
1440
- await this.cache.removeStoreAsync(userInfoKey)
1441
- this.setAccessKey()
1246
+ // 参数校验:email或phone必填其一
1247
+ this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
1442
1248
 
1443
- this.config.eventBus?.fire(EVENTS.LOGIN_STATE_CHANGED, { eventType: LOGIN_STATE_CHANGED_TYPE.SIGN_OUT })
1249
+ const target = params.type === 'signup' ? 'ANY' : 'USER'
1250
+ const data: { email?: string; phone_number?: string; target: 'USER' | 'ANY' } = { target }
1251
+ if ('email' in params) {
1252
+ data.email = params.email
1253
+ }
1444
1254
 
1445
- this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_OUT })
1255
+ if ('phone' in params) {
1256
+ data.phone_number = this.formatPhone(params.phone)
1257
+ }
1446
1258
 
1447
- // res返回是为了兼容v2版本
1448
- return { ...res, data: {}, error: null }
1449
- } catch (error) {
1450
- return { data: {}, error: new AuthError(error) }
1259
+ // 重新发送验证码
1260
+ const { verification_id: verificationId } = await this.oauthInstance.authApi.getVerification(data)
1261
+
1262
+ return {
1263
+ data: { messageId: verificationId },
1264
+ error: null,
1265
+ }
1266
+ } catch (error: any) {
1267
+ return {
1268
+ data: {},
1269
+ error: new AuthError(error),
1270
+ }
1451
1271
  }
1452
1272
  }
1453
1273
 
1454
1274
  /**
1455
- * https://supabase.com/docs/reference/javascript/auth-onauthstatechange
1456
- * Receive a notification every time an auth event happens.
1457
- * @param callback
1458
- * @returns Promise<{ data: { subscription: Subscription }, error: Error | null }>
1275
+ * https://supabase.com/docs/reference/javascript/auth-setsession
1276
+ * 使用access_token和refresh_token来设置会话
1277
+ * @param params
1278
+ * @returns Promise<SignInRes>
1459
1279
  */
1460
- onAuthStateChange(callback: OnAuthStateChangeCallback) {
1461
- if (!this.hasListenerSetUp) {
1462
- this.setupListeners()
1463
- this.hasListenerSetUp = true
1464
- }
1280
+ async setSession(params: SetSessionReq): Promise<SignInRes> {
1281
+ try {
1282
+ this.validateParams(params, {
1283
+ access_token: { required: true, message: 'Access token is required' },
1284
+ refresh_token: { required: true, message: 'Refresh token is required' },
1285
+ })
1465
1286
 
1466
- const id = Math.random().toString(36)
1287
+ await this.oauthInstance.oauth2client.refreshToken(params, { throwError: true })
1467
1288
 
1468
- if (!this.listeners.has(id)) {
1469
- this.listeners.set(id, new Set())
1470
- }
1289
+ const { data: { session } = {} } = await this.getSession()
1471
1290
 
1472
- this.listeners.get(id)!.add(callback)
1291
+ this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_IN })
1473
1292
 
1474
- // 返回 Subscription 对象
1475
- const subscription = {
1476
- id,
1477
- callback,
1478
- unsubscribe: () => {
1479
- const callbacks = this.listeners.get(id)
1480
- if (callbacks) {
1481
- callbacks.delete(callback)
1482
- if (callbacks.size === 0) {
1483
- this.listeners.delete(id)
1484
- }
1485
- }
1486
- },
1293
+ return { data: { user: session.user, session }, error: null }
1294
+ } catch (error) {
1295
+ return { data: {}, error: new AuthError(error) }
1487
1296
  }
1297
+ }
1488
1298
 
1489
- return {
1490
- data: { subscription },
1491
- }
1299
+ // https://supabase.com/docs/reference/javascript/auth-exchangecodeforsession
1300
+ async exchangeCodeForSession() {
1301
+ //
1492
1302
  }
1493
1303
 
1494
1304
  /**
1495
- * https://supabase.com/docs/reference/javascript/auth-signinwithpassword
1496
- * Log in an existing user with an email and password or phone and password or username and password.
1305
+ * 删除当前用户
1497
1306
  * @param params
1498
- * @returns Promise<SignInRes>
1307
+ * @returns
1499
1308
  */
1500
- async signInWithPassword(params: SignInWithPasswordReq): Promise<SignInRes> {
1309
+ async deleteUser(params: DeleteMeReq): Promise<CommonRes> {
1501
1310
  try {
1502
- // 参数校验:username/email/phone三选一,password必填
1503
- this.validateAtLeastOne(
1504
- params,
1505
- [['username'], ['email'], ['phone']],
1506
- 'You must provide either username, email, or phone',
1507
- )
1508
1311
  this.validateParams(params, {
1509
1312
  password: { required: true, message: 'Password is required' },
1510
1313
  })
1511
1314
 
1512
- await this.signIn({
1513
- username: params.username || params.email || this.formatPhone(params.phone),
1514
- password: params.password,
1515
- ...(params.is_encrypt ? { isEncrypt: true, version: 'v2' } : {}),
1516
- })
1517
- const { data: { session } = {} } = await this.getSession()
1315
+ const { sudo_token } = await this.oauthInstance.authApi.sudo(params)
1518
1316
 
1519
- return { data: { user: session.user, session }, error: null }
1317
+ await this.oauthInstance.authApi.deleteMe({ sudo_token })
1318
+
1319
+ return { data: {}, error: null }
1520
1320
  } catch (error) {
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 }
1321
+ return { data: {}, error: new AuthError(error) }
1531
1322
  }
1532
1323
  }
1533
1324
 
1534
1325
  /**
1535
- * https://supabase.com/docs/reference/javascript/auth-signinwithidtoken
1536
- * 第三方平台登录。如果用户不存在,会根据云开发平台-登录方式中对应身份源的登录模式配置,判断是否自动注册
1537
- * @param params
1538
- * @returns Promise<SignInRes>
1326
+ * 跳转系统默认登录页
1327
+ * @returns {Promise<authModels.ToDefaultLoginPage>}
1328
+ * @memberof Auth
1539
1329
  */
1540
- async signInWithIdToken(params: SignInWithIdTokenReq): Promise<SignInRes> {
1330
+ async toDefaultLoginPage(params: authModels.ToDefaultLoginPage = {}): Promise<CommonRes> {
1541
1331
  try {
1542
- // 参数校验:token必填
1543
- this.validateParams(params, {
1544
- token: { required: true, message: 'Token is required' },
1545
- })
1546
-
1547
- await this.signInWithProvider({
1548
- provider_token: params.token,
1549
- })
1550
- const { data: { session } = {} } = await this.getSession()
1332
+ const configVersion = params.config_version || 'env'
1333
+ const query = Object.keys(params.query || {})
1334
+ .map(key => `${key}=${params.query[key]}`)
1335
+ .join('&')
1551
1336
 
1552
- return { data: { user: session.user, session }, error: null }
1337
+ if (adapterForWxMp.isMatch()) {
1338
+ wx.navigateTo({ url: `/packages/$wd_system/pages/login/index${query ? `?${query}` : ''}` })
1339
+ } else {
1340
+ const redirectUri = params.redirect_uri || window.location.href
1341
+ const urlObj = new URL(redirectUri)
1342
+ const loginPage = `${urlObj.origin}/__auth/?app_id=${params.app_id || ''}&env_id=${this.config.env}&client_id=${
1343
+ this.config.clientId || this.config.env
1344
+ }&config_version=${configVersion}&redirect_uri=${encodeURIComponent(redirectUri)}${query ? `&${query}` : ''}`
1345
+ window.location.href = loginPage
1346
+ }
1347
+ return { data: {}, error: null }
1553
1348
  } catch (error) {
1554
1349
  return { data: {}, error: new AuthError(error) }
1555
1350
  }
1556
1351
  }
1557
1352
 
1558
1353
  /**
1559
- * https://supabase.com/docs/reference/javascript/auth-signinwithotp
1560
- * Log in a user using a one-time password (OTP).
1561
- * @param params
1562
- * @returns Promise<SignInWithOtpRes>
1354
+ * 自定义登录
1355
+ * @param getTickFn () => Promise<string>, 获取自定义登录 ticket 的函数
1356
+ * @returns
1563
1357
  */
1564
- async signInWithOtp(params: SignInWithOtpReq): Promise<SignInWithOtpRes> {
1565
- if (params.options?.shouldCreateUser === undefined || !!params.options?.shouldCreateUser) {
1566
- return this.signUp({
1567
- email: params.email,
1568
- phone: params.phone,
1569
- })
1358
+ async signInWithCustomTicket(getTickFn?: authModels.GetCustomSignTicketFn): Promise<SignInRes> {
1359
+ if (getTickFn) {
1360
+ this.setCustomSignFunc(getTickFn)
1570
1361
  }
1571
1362
 
1572
1363
  try {
1573
- // 参数校验:email或phone必填其一
1574
- this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
1364
+ await this.oauthInstance.authApi.signInWithCustomTicket()
1365
+ const loginState = await this.createLoginState()
1575
1366
 
1576
- // 第一步:发送验证码并存储 verificationInfo
1577
- const verificationInfo = await this.getVerification(params.email ? { email: params.email } : { phone_number: this.formatPhone(params.phone) },)
1367
+ const { data: { session } = {} } = await this.getSession()
1578
1368
 
1579
- return {
1580
- data: {
1581
- user: null,
1582
- session: null,
1583
- // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
1584
- verifyOtp: async ({ token, messageId = verificationInfo.verification_id }): Promise<SignInRes> => this.verifyOtp({
1585
- email: params.email,
1586
- phone: params.phone,
1587
- token,
1588
- messageId,
1589
- }),
1590
- },
1591
- error: null,
1592
- }
1369
+ // loginState返回是为了兼容v2版本
1370
+ return { ...(loginState as any), data: { user: session.user, session }, error: null }
1593
1371
  } catch (error) {
1594
1372
  return { data: {}, error: new AuthError(error) }
1595
1373
  }
1596
1374
  }
1597
1375
 
1598
1376
  /**
1599
- * 校验第三方平台授权登录回调
1377
+ * 小程序openId静默登录
1600
1378
  * @param params
1601
1379
  * @returns Promise<SignInRes>
1602
1380
  */
1603
- async verifyOAuth(params?: VerifyOAuthReq): Promise<VerifyOAuthRes> {
1604
- const data: any = {}
1605
- try {
1606
- // 回调至 provider_redirect_uri 地址(url query中携带 授权code,state等参数),此时检查 state 是否符合预期(如 自己设置的 wx_open)
1607
- const code = params?.code || utils.getQuery('code')
1608
- const state = params?.state || utils.getQuery('state')
1381
+ async signInWithOpenId({ useWxCloud = true } = {}): Promise<SignInRes> {
1382
+ if (!adapterForWxMp.isMatch()) {
1383
+ throw Error('wx api undefined')
1384
+ }
1385
+ const wxInfo = wx.getAccountInfoSync().miniProgram
1609
1386
 
1610
- // 参数校验:code和state必填
1611
- if (!code) {
1612
- return { data: {}, error: new AuthError({ message: 'Code is required' }) }
1613
- }
1387
+ const mainFunc = async (code) => {
1388
+ let result: authModels.GrantProviderTokenResponse | undefined = undefined
1389
+ let credentials: Credentials | undefined = undefined
1614
1390
 
1615
- if (!state) {
1616
- return { data: {}, error: new AuthError({ message: 'State is required' }) }
1617
- }
1391
+ try {
1392
+ result = await this.oauthInstance.authApi.grantProviderToken(
1393
+ {
1394
+ provider_id: wxInfo?.appId,
1395
+ provider_code: code,
1396
+ provider_params: {
1397
+ provider_code_type: 'open_id',
1398
+ appid: wxInfo?.appId,
1399
+ },
1400
+ },
1401
+ useWxCloud,
1402
+ )
1618
1403
 
1619
- const cacheData = getBrowserSession(state)
1620
- data.type = cacheData?.type
1404
+ if ((result as any)?.error_code || !result.provider_token) {
1405
+ throw result
1406
+ }
1621
1407
 
1622
- const provider = params?.provider || cacheData?.provider || utils.getQuery('provider')
1408
+ credentials = await this.oauthInstance.authApi.signInWithProvider(
1409
+ { provider_token: result.provider_token },
1410
+ useWxCloud,
1411
+ )
1623
1412
 
1624
- if (!provider) {
1625
- return { data, error: new AuthError({ message: 'Provider is required' }) }
1413
+ if ((credentials as any)?.error_code) {
1414
+ throw credentials
1415
+ }
1416
+ } catch (error) {
1417
+ throw error
1626
1418
  }
1419
+ await this.oauthInstance.oauth2client.setCredentials(credentials as Credentials)
1420
+ }
1627
1421
 
1628
- // state符合预期,则获取该三方平台token
1629
- const { provider_token: token } = await this.grantProviderToken({
1630
- provider_id: provider,
1631
- provider_redirect_uri: location.origin + location.pathname, // 指定三方平台跳回的 url 地址
1632
- provider_code: code, // 第三方平台跳转回页面时,url param 中携带的 code 参数
1422
+ try {
1423
+ await new Promise((resolve, reject) => {
1424
+ wx.login({
1425
+ success: async (res: { code: string }) => {
1426
+ try {
1427
+ await mainFunc(res.code)
1428
+ resolve(true)
1429
+ } catch (error) {
1430
+ reject(error)
1431
+ }
1432
+ },
1433
+ fail: (res: any) => {
1434
+ const error = new Error(res?.errMsg)
1435
+ ;(error as any).code = res?.errno
1436
+ reject(error)
1437
+ },
1438
+ })
1633
1439
  })
1634
1440
 
1635
- let res: VerifyOAuthRes
1636
-
1637
- if (cacheData.type === OAUTH_TYPE.BIND_IDENTITY) {
1638
- res = await this.oauthInstance.authApi.toBindIdentity({ provider_token: token, provider, fireEvent: true })
1639
- } else {
1640
- // 通过 provider_token 仅登录或登录并注册(与云开发平台-登录方式-身份源登录模式配置有关)
1641
- res = await this.signInWithIdToken({
1642
- token,
1643
- })
1644
- res.data = { ...data, ...res.data }
1645
- }
1441
+ const loginState = await this.createLoginState()
1646
1442
 
1647
- const localSearch = new URLSearchParams(location?.search)
1648
- localSearch.delete('code')
1649
- localSearch.delete('state')
1650
- res.data.redirectUrl = addUrlSearch(
1651
- cacheData?.search === undefined ? `?${localSearch.toString()}` : cacheData?.search,
1652
- cacheData?.hash || location.hash,
1653
- )
1654
- removeBrowserSession(state)
1443
+ const { data: { session } = {} } = await this.getSession()
1655
1444
 
1656
- return res
1445
+ // loginState返回是为了兼容v2版本
1446
+ return { ...(loginState as any), data: { user: session.user, session }, error: null }
1657
1447
  } catch (error) {
1658
- return { data, error: new AuthError(error) }
1448
+ return { data: {}, error: new AuthError(error) }
1659
1449
  }
1660
1450
  }
1661
1451
 
1662
1452
  /**
1663
- * https://supabase.com/docs/reference/javascript/auth-signinwithoauth
1664
- * 生成第三方平台授权 Uri (如微信二维码扫码授权网页)
1453
+ * 小程序手机号授权登录
1665
1454
  * @param params
1666
- * @returns Promise<SignInOAuthRes>
1455
+ * @returns Promise<SignInRes>
1667
1456
  */
1668
- async signInWithOAuth(params: SignInWithOAuthReq): Promise<SignInOAuthRes> {
1669
- try {
1670
- // 参数校验:provider必填
1671
- this.validateParams(params, {
1672
- provider: { required: true, message: 'Provider is required' },
1673
- })
1457
+ // async signInWithPhoneAuth({ phoneCode = '', useWxCloud = false }): Promise<SignInRes> {
1458
+ async signInWithPhoneAuth({ phoneCode = '' }): Promise<SignInRes> {
1459
+ // if (!adapterForWxMp.isMatch()) {
1460
+ // return { data: {}, error: new AuthError({ message: 'wx api undefined' }) }
1461
+ // }
1462
+ // const wxInfo = wx.getAccountInfoSync().miniProgram
1463
+ // const providerInfo = {
1464
+ // provider_params: { provider_code_type: 'phone', code: phoneCode, appid: wxInfo.appId },
1465
+ // provider_id: wxInfo.appId,
1466
+ // }
1674
1467
 
1675
- const href = params.options?.redirectTo || location.href
1468
+ // const { code } = await wx.login()
1469
+ // ;(providerInfo as any).provider_code = code
1676
1470
 
1677
- const urlObject = new URL(href)
1471
+ // try {
1472
+ // const providerToken = await this.oauthInstance.authApi.grantProviderToken(providerInfo, useWxCloud)
1473
+ // if (providerToken.error_code) {
1474
+ // throw providerToken
1475
+ // }
1678
1476
 
1679
- const provider_redirect_uri = urlObject.origin + urlObject.pathname
1477
+ // const signInRes = await this.oauthInstance.authApi.signInWithProvider({
1478
+ // provider_token: providerToken.provider_token,
1479
+ // }, useWxCloud)
1680
1480
 
1681
- const state = params.options?.state || `prd-${params.provider}-${Math.random().toString(36)
1682
- .slice(2)}`
1481
+ // if ((signInRes as any)?.error_code) {
1482
+ // throw signInRes
1483
+ // }
1484
+ // } catch (error) {
1485
+ // return { data: {}, error: new AuthError(error) }
1486
+ // }
1683
1487
 
1684
- const { uri } = await this.genProviderRedirectUri({
1685
- provider_id: params.provider,
1686
- provider_redirect_uri,
1687
- state,
1688
- })
1488
+ // const loginState = await this.createLoginState()
1689
1489
 
1690
- // URL 进行解码
1691
- const decodedUri = decodeURIComponent(uri)
1490
+ // const { data: { session } = {} } = await this.getSession()
1692
1491
 
1693
- // 合并额外的查询参数
1694
- let finalUri = decodedUri
1492
+ // // loginState返回是为了兼容v2版本
1493
+ // return { ...(loginState as any), data: { user: session.user, session }, error: null }
1494
+ if (!adapterForWxMp.isMatch()) {
1495
+ return { data: {}, error: new AuthError({ message: 'wx api undefined' }) }
1496
+ }
1497
+ const wxInfo = wx.getAccountInfoSync().miniProgram
1498
+ const providerInfo = {
1499
+ provider_params: { provider_code_type: 'phone' },
1500
+ provider_id: wxInfo.appId,
1501
+ }
1695
1502
 
1696
- if (params.options?.queryParams) {
1697
- const url = new URL(decodedUri)
1698
- Object.entries(params.options.queryParams).forEach(([key, value]) => {
1699
- url.searchParams.set(key, value)
1700
- })
1701
- finalUri = url.toString()
1503
+ const { code } = await wx.login()
1504
+ ;(providerInfo as any).provider_code = code
1505
+
1506
+ try {
1507
+ let providerToken = await this.oauthInstance.authApi.grantProviderToken(providerInfo)
1508
+ if (providerToken.error_code) {
1509
+ throw providerToken
1702
1510
  }
1703
1511
 
1704
- saveToBrowserSession(state, {
1705
- provider: params.provider,
1706
- search: urlObject.search,
1707
- hash: urlObject.hash,
1708
- type: params.options?.type || OAUTH_TYPE.SIGN_IN,
1512
+ providerToken = await this.oauthInstance.authApi.patchProviderToken({
1513
+ provider_token: providerToken.provider_token,
1514
+ provider_id: wxInfo.appId,
1515
+ provider_params: {
1516
+ code: phoneCode,
1517
+ provider_code_type: 'phone',
1518
+ },
1709
1519
  })
1710
-
1711
- if (isBrowser() && !params.options?.skipBrowserRedirect) {
1712
- window.location.assign(finalUri)
1520
+ if (providerToken.error_code) {
1521
+ throw providerToken
1713
1522
  }
1714
1523
 
1715
- return { data: { url: finalUri, provider: params.provider }, error: null }
1524
+ const signInRes = await this.oauthInstance.authApi.signInWithProvider({
1525
+ provider_token: providerToken.provider_token,
1526
+ })
1527
+
1528
+ if ((signInRes as any)?.error_code) {
1529
+ throw signInRes
1530
+ }
1716
1531
  } catch (error) {
1717
1532
  return { data: {}, error: new AuthError(error) }
1718
1533
  }
1534
+
1535
+ const loginState = await this.createLoginState()
1536
+
1537
+ const { data: { session } = {} } = await this.getSession()
1538
+
1539
+ // loginState返回是为了兼容v2版本
1540
+ return { ...(loginState as any), data: { user: session.user, session }, error: null }
1719
1541
  }
1720
1542
 
1721
- // https://supabase.com/docs/reference/javascript/auth-getclaims
1722
- async getClaims(): Promise<GetClaimsRes> {
1723
- try {
1724
- const { accessToken } = await this.getAccessToken()
1725
- const parsedToken = weAppJwtDecodeAll(accessToken)
1726
- return { data: parsedToken, error: null }
1727
- } catch (error) {
1728
- return { data: {}, error: new AuthError(error) }
1543
+ /**
1544
+ * 绑定手机号
1545
+ * @param phoneNumber
1546
+ * @param phoneCode
1547
+ */
1548
+ @catchErrorsDecorator({
1549
+ title: '绑定手机号失败',
1550
+ messages: [
1551
+ '请确认以下各项:',
1552
+ ' 1 - 调用 auth().bindPhoneNumber() 的语法或参数是否正确',
1553
+ ' 2 - 当前环境是否开通了短信验证码登录',
1554
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1555
+ ],
1556
+ })
1557
+ public async bindPhoneNumber(params: authModels.BindPhoneRequest) {
1558
+ return this.oauthInstance.authApi.editContact(params)
1559
+ }
1560
+
1561
+ /**
1562
+ * 解绑三方绑定
1563
+ * @param loginType
1564
+ */
1565
+ @catchErrorsDecorator({
1566
+ title: '解除三方绑定失败',
1567
+ messages: [
1568
+ '请确认以下各项:',
1569
+ ' 1 - 调用 auth().unbindProvider() 的语法或参数是否正确',
1570
+ ' 2 - 当前账户是否已经与此登录方式解绑',
1571
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1572
+ ],
1573
+ })
1574
+ public async unbindProvider(params: authModels.UnbindProviderRequest): Promise<void> {
1575
+ return this.oauthInstance.authApi.unbindProvider(params)
1576
+ }
1577
+
1578
+ /**
1579
+ * 更新邮箱地址
1580
+ * @param email
1581
+ * @param sudo_token
1582
+ * @param verification_token
1583
+ */
1584
+ @catchErrorsDecorator({
1585
+ title: '绑定邮箱地址失败',
1586
+ messages: [
1587
+ '请确认以下各项:',
1588
+ ' 1 - 调用 auth().bindEmail() 的语法或参数是否正确',
1589
+ ' 2 - 当前环境是否开通了邮箱密码登录',
1590
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1591
+ ],
1592
+ })
1593
+ public bindEmail(params: authModels.BindEmailRequest) {
1594
+ return this.oauthInstance.authApi.editContact(params)
1595
+ }
1596
+
1597
+ /**
1598
+ * verify
1599
+ * @param {authModels.VerifyRequest} params
1600
+ * @returns {Promise<authModels.VerifyResponse>}
1601
+ * @memberof User
1602
+ */
1603
+ @catchErrorsDecorator({
1604
+ title: '验证码验证失败',
1605
+ messages: [
1606
+ '请确认以下各项:',
1607
+ ' 1 - 调用 auth().verify() 的语法或参数是否正确',
1608
+ ' 2 - 当前环境是否开通了手机验证码/邮箱登录',
1609
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1610
+ ],
1611
+ })
1612
+ public async verify(params: authModels.VerifyRequest): Promise<authModels.VerifyResponse> {
1613
+ return this.oauthInstance.authApi.verify(params)
1614
+ }
1615
+
1616
+ /**
1617
+ * 获取验证码
1618
+ * @param {authModels.GetVerificationRequest} params
1619
+ * @returns {Promise<authModels.GetVerificationResponse>}
1620
+ * @memberof User
1621
+ */
1622
+ @catchErrorsDecorator({
1623
+ title: '获取验证码失败',
1624
+ messages: [
1625
+ '请确认以下各项:',
1626
+ ' 1 - 调用 auth().getVerification() 的语法或参数是否正确',
1627
+ ' 2 - 当前环境是否开通了手机验证码/邮箱登录',
1628
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1629
+ ],
1630
+ })
1631
+ public async getVerification(
1632
+ params: authModels.GetVerificationRequest,
1633
+ options?: { withCaptcha: boolean },
1634
+ ): Promise<authModels.GetVerificationResponse> {
1635
+ if (params.phone_number) {
1636
+ params.phone_number = this.formatPhone(params.phone_number)
1729
1637
  }
1638
+ return this.oauthInstance.authApi.getVerification(params, options)
1730
1639
  }
1731
1640
 
1732
1641
  /**
1733
- * https://supabase.com/docs/reference/javascript/auth-resetpasswordforemail
1734
- * 通过 email 或手机号重置密码
1735
- * @param emailOrPhone 邮箱或手机号
1736
- * @returns Promise<ResetPasswordForEmailRes>
1642
+ * 获取当前登录的用户信息-同步
1737
1643
  */
1738
- async resetPasswordForEmail(
1739
- emailOrPhone: string,
1740
- options?: { redirectTo?: string },
1741
- ): Promise<ResetPasswordForEmailRes> {
1742
- try {
1743
- // 参数校验:emailOrPhone必填
1744
- this.validateParams(
1745
- { emailOrPhone },
1746
- {
1747
- emailOrPhone: { required: true, message: 'Email or phone is required' },
1748
- },
1644
+ get currentUser() {
1645
+ if (this.cache.mode === 'async') {
1646
+ // async storage的平台调用此API提示
1647
+ printWarn(
1648
+ ERRORS.INVALID_OPERATION,
1649
+ 'current platform\'s storage is asynchronous, please use getCurrentUser instead',
1749
1650
  )
1651
+ return
1652
+ }
1750
1653
 
1751
- const { redirectTo } = options || {}
1752
-
1753
- // 判断是邮箱还是手机号
1754
- const isEmail = emailOrPhone.includes('@')
1755
- let verificationParams: { email?: string; phone_number?: string }
1654
+ const loginState = this.hasLoginState()
1756
1655
 
1757
- if (isEmail) {
1758
- verificationParams = { email: emailOrPhone }
1759
- } else {
1760
- // 正规化手机号
1761
- const formattedPhone = this.formatPhone(emailOrPhone)
1762
- verificationParams = { phone_number: formattedPhone }
1763
- }
1656
+ if (loginState) {
1657
+ return loginState.user || null
1658
+ }
1659
+ return null
1660
+ }
1764
1661
 
1765
- // 第一步:发送验证码并存储 verificationInfo
1766
- const verificationInfo = await this.getVerification(verificationParams)
1662
+ /**
1663
+ * 获取当前登录的用户信息-异步
1664
+ */
1665
+ @catchErrorsDecorator({
1666
+ title: '获取用户信息失败',
1667
+ messages: [
1668
+ '请确认以下各项:',
1669
+ ' 1 - 调用 auth().getCurrentUser() 的语法或参数是否正确',
1670
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1671
+ ],
1672
+ })
1673
+ public async getCurrentUser(): Promise<(authModels.UserInfo & Partial<User>) | null> {
1674
+ const loginState = await this.getLoginState()
1675
+ if (loginState) {
1676
+ const userInfo = loginState.user.getLocalUserInfo() as authModels.UserInfo
1677
+ await loginState.user.checkLocalInfoAsync()
1678
+ return { ...loginState.user, ...userInfo } as unknown as authModels.UserInfo & Partial<User>
1679
+ }
1680
+ return null
1681
+ }
1767
1682
 
1768
- return {
1769
- data: {
1770
- // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
1771
- updateUser: async (attributes: UpdateUserAttributes): Promise<SignInRes> => {
1772
- this.validateParams(attributes, {
1773
- nonce: { required: true, message: 'Nonce is required' },
1774
- password: { required: true, message: 'Password is required' },
1775
- })
1776
- try {
1777
- // 第三步:待用户输入完验证码之后,验证验证码
1778
- const verificationTokenRes = await this.verify({
1779
- verification_id: verificationInfo.verification_id,
1780
- verification_code: attributes.nonce,
1781
- })
1683
+ /**
1684
+ * 匿名登录
1685
+ * @returns {Promise<LoginState>}
1686
+ * @memberof Auth
1687
+ */
1688
+ @catchErrorsDecorator({
1689
+ title: '小程序匿名登录失败',
1690
+ messages: [
1691
+ '请确认以下各项:',
1692
+ ' 1 - 当前环境是否开启了匿名登录',
1693
+ ' 2 - 调用 auth().signInAnonymouslyInWx() 的语法或参数是否正确',
1694
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1695
+ ],
1696
+ })
1697
+ public async signInAnonymouslyInWx({
1698
+ useWxCloud,
1699
+ }: {
1700
+ useWxCloud?: boolean
1701
+ } = {}): Promise<LoginState> {
1702
+ if (!adapterForWxMp.isMatch()) {
1703
+ throw Error('wx api undefined')
1704
+ }
1705
+ const wxInfo = wx.getAccountInfoSync().miniProgram
1782
1706
 
1783
- await this.oauthInstance.authApi.resetPassword({
1784
- email: isEmail ? emailOrPhone : undefined,
1785
- phone_number: !isEmail ? emailOrPhone : undefined,
1786
- new_password: attributes.password,
1787
- verification_token: verificationTokenRes.verification_token,
1788
- })
1707
+ const mainFunc = async (code) => {
1708
+ let result: authModels.GrantProviderTokenResponse | undefined = undefined
1709
+ let credentials: Credentials | undefined = undefined
1789
1710
 
1790
- this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, {
1791
- event: AUTH_STATE_CHANGED_TYPE.PASSWORD_RECOVERY,
1792
- })
1711
+ try {
1712
+ result = await this.oauthInstance.authApi.grantProviderToken(
1713
+ {
1714
+ provider_id: wxInfo?.appId,
1715
+ provider_code: code,
1716
+ provider_params: {
1717
+ provider_code_type: 'open_id',
1718
+ appid: wxInfo?.appId,
1719
+ },
1720
+ },
1721
+ useWxCloud,
1722
+ )
1793
1723
 
1794
- const res = await this.signInWithPassword({
1795
- email: isEmail ? emailOrPhone : undefined,
1796
- phone: !isEmail ? emailOrPhone : undefined,
1797
- password: attributes.password,
1798
- })
1724
+ if ((result as any)?.error_code || !result.provider_token) {
1725
+ throw result
1726
+ }
1799
1727
 
1800
- if (redirectTo && isBrowser()) {
1801
- window.location.assign(redirectTo)
1802
- }
1728
+ credentials = await this.oauthInstance.authApi.signInAnonymously(
1729
+ { provider_token: result.provider_token },
1730
+ useWxCloud,
1731
+ )
1803
1732
 
1804
- return res
1805
- } catch (error) {
1806
- return { data: {}, error: new AuthError(error) }
1807
- }
1808
- },
1809
- },
1810
- error: null,
1733
+ if ((credentials as any)?.error_code) {
1734
+ throw credentials
1735
+ }
1736
+ } catch (error) {
1737
+ throw error
1811
1738
  }
1812
- } catch (error) {
1813
- return { data: {}, error: new AuthError(error) }
1814
1739
  }
1815
- }
1816
1740
 
1817
- /**
1818
- * 通过旧密码重置密码
1819
- * @param new_password
1820
- * @param old_password
1821
- * @returns
1822
- */
1823
- async resetPasswordForOld(params: ResetPasswordForOldReq) {
1824
- try {
1825
- await this.oauthInstance.authApi.updatePasswordByOld({
1826
- old_password: params.old_password,
1827
- new_password: params.new_password,
1741
+ await new Promise((resolve, reject) => {
1742
+ wx.login({
1743
+ success: async (res: { code: string }) => {
1744
+ try {
1745
+ await mainFunc(res.code)
1746
+ resolve(true)
1747
+ } catch (error) {
1748
+ reject(error)
1749
+ }
1750
+ },
1751
+ fail: (res: any) => {
1752
+ const error = new Error(res?.errMsg)
1753
+ ;(error as any).code = res?.errno
1754
+ reject(error)
1755
+ },
1828
1756
  })
1757
+ })
1829
1758
 
1830
- const { data: { session } = {} } = await this.getSession()
1831
-
1832
- return { data: { user: session.user, session }, error: null }
1833
- } catch (error) {
1834
- return { data: {}, error: new AuthError(error) }
1835
- }
1759
+ return this.createLoginState(undefined, { asyncRefreshUser: true })
1836
1760
  }
1837
1761
 
1838
1762
  /**
1839
- * https://supabase.com/docs/reference/javascript/auth-verifyotp
1840
- * Log in a user given a User supplied OTP and verificationId received through mobile or email.
1841
- * @param params
1842
- * @returns Promise<SignInRes>
1763
+ * 小程序绑定OpenID
1764
+ * @returns {Promise<LoginState>}
1765
+ * @memberof Auth
1843
1766
  */
1844
- async verifyOtp(params: VerifyOtpReq): Promise<SignInRes> {
1845
- try {
1846
- const { type } = params
1847
- // 参数校验:token和verificationInfo必填
1848
- this.validateParams(params, {
1849
- token: { required: true, message: 'Token is required' },
1850
- messageId: { required: true, message: 'messageId is required' },
1851
- })
1767
+ @catchErrorsDecorator({
1768
+ title: '小程序绑定OpenID失败',
1769
+ messages: [
1770
+ '请确认以下各项:',
1771
+ ' 1 - 当前环境是否开启了小程序openId静默登录',
1772
+ ' 2 - 调用 auth().bindOpenId() 的语法或参数是否正确',
1773
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1774
+ ],
1775
+ })
1776
+ public async bindOpenId(): Promise<void> {
1777
+ if (!adapterForWxMp.isMatch()) {
1778
+ throw Error('wx api undefined')
1779
+ }
1780
+ const wxInfo = wx.getAccountInfoSync().miniProgram
1852
1781
 
1853
- if (['phone_change', 'email_change'].includes(type)) {
1854
- await this.verify({
1855
- verification_id: params.messageId,
1856
- verification_code: params.token,
1857
- })
1858
- } else {
1859
- await this.signInWithUsername({
1860
- verificationInfo: { verification_id: params.messageId, is_user: true },
1861
- verificationCode: params.token,
1862
- username: params.email || this.formatPhone(params.phone) || '',
1863
- loginType: params.email ? 'email' : 'phone',
1782
+ const mainFunc = async (code) => {
1783
+ let result: authModels.GrantProviderTokenResponse | undefined = undefined
1784
+
1785
+ try {
1786
+ result = await this.oauthInstance.authApi.grantProviderToken({
1787
+ provider_id: wxInfo?.appId,
1788
+ provider_code: code,
1789
+ provider_params: {
1790
+ provider_code_type: 'open_id',
1791
+ appid: wxInfo?.appId,
1792
+ },
1864
1793
  })
1794
+
1795
+ if ((result as any)?.error_code || !result.provider_token) {
1796
+ throw result
1797
+ }
1798
+
1799
+ await this.oauthInstance.authApi.bindWithProvider({ provider_token: result.provider_token })
1800
+ } catch (error) {
1801
+ throw error
1865
1802
  }
1803
+ }
1866
1804
 
1867
- const { data: { session } = {} } = await this.getSession()
1805
+ await new Promise((resolve, reject) => {
1806
+ wx.login({
1807
+ success: async (res: { code: string }) => {
1808
+ try {
1809
+ await mainFunc(res.code)
1810
+ resolve(true)
1811
+ } catch (error) {
1812
+ reject(error)
1813
+ }
1814
+ },
1815
+ fail: (res: any) => {
1816
+ const error = new Error(res?.errMsg)
1817
+ ;(error as any).code = res?.errno
1818
+ reject(error)
1819
+ },
1820
+ })
1821
+ })
1868
1822
 
1869
- return { data: { user: session.user, session }, error: null }
1870
- } catch (error) {
1871
- return { data: {}, error: new AuthError(error) }
1872
- }
1823
+ return
1873
1824
  }
1874
1825
 
1875
1826
  /**
1876
- * https://supabase.com/docs/reference/javascript/auth-getSession
1877
- * Returns the session, refreshing it if necessary.
1878
- * @returns Promise<SignInRes>
1827
+ * 小程序unionId静默登录
1828
+ * @returns {Promise<LoginState>}
1829
+ * @memberof Auth
1879
1830
  */
1880
- async getSession(): Promise<SignInRes> {
1831
+ @catchErrorsDecorator({
1832
+ title: '小程序unionId静默登录失败',
1833
+ messages: [
1834
+ '请确认以下各项:',
1835
+ ' 1 - 当前环境是否开启了小程序unionId静默登录',
1836
+ ' 2 - 调用 auth().signInWithUnionId() 的语法或参数是否正确',
1837
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1838
+ ],
1839
+ })
1840
+ public async signInWithUnionId(): Promise<LoginState> {
1841
+ if (!adapterForWxMp.isMatch()) {
1842
+ throw Error('wx api undefined')
1843
+ }
1881
1844
  try {
1882
- const credentials: Credentials = await this.oauthInstance.oauth2client.getCredentials()
1883
-
1884
- if (!credentials || credentials.scope === 'accessKey') {
1885
- return { data: { session: null }, error: null }
1886
- }
1845
+ await new Promise((resolve, reject) => {
1846
+ const wxInfo = wx.getAccountInfoSync().miniProgram
1847
+ wx.login({
1848
+ success: async (res: { code: string }) => {
1849
+ const providerId = wxInfo?.appId
1850
+ try {
1851
+ const result = await this.oauthInstance.authApi.grantProviderToken({
1852
+ provider_code: res.code,
1853
+ provider_id: providerId,
1854
+ provider_params: {
1855
+ provider_code_type: 'union_id',
1856
+ appid: wxInfo?.appId,
1857
+ },
1858
+ })
1887
1859
 
1888
- const { data: { user } = {} } = await this.getUser()
1860
+ const { provider_token: providerToken } = result
1889
1861
 
1890
- return { data: { session: { ...credentials, user }, user }, error: null }
1891
- } catch (error) {
1892
- return { data: {}, error: new AuthError(error) }
1893
- }
1894
- }
1862
+ if (!providerToken) {
1863
+ reject(result)
1864
+ return
1865
+ }
1895
1866
 
1896
- /**
1897
- * https://supabase.com/docs/reference/javascript/auth-refreshsession
1898
- * 无论过期状态如何,都返回一个新的会话
1899
- * @param refresh_token
1900
- * @returns Promise<SignInRes>
1901
- */
1902
- async refreshSession(refresh_token?: string): Promise<SignInRes> {
1903
- try {
1904
- const credentials: Credentials = await this.oauthInstance.oauth2client.localCredentials.getCredentials()
1905
- credentials.refresh_token = refresh_token || credentials.refresh_token
1906
- const newTokens = await this.oauthInstance.oauth2client.refreshToken(credentials)
1907
- const { data: { user } = {} } = await this.getUser()
1867
+ const signInRes = await this.oauthInstance.authApi.signInWithProvider({
1868
+ provider_id: providerId,
1869
+ provider_token: providerToken,
1870
+ })
1908
1871
 
1909
- return { data: { user, session: { ...newTokens, user } }, error: null }
1872
+ if ((signInRes as any)?.error_code) {
1873
+ reject(signInRes)
1874
+ return
1875
+ }
1876
+ resolve(true)
1877
+ } catch (error) {
1878
+ reject(error)
1879
+ }
1880
+ },
1881
+ fail: (res: any) => {
1882
+ const error = new Error(res?.errMsg)
1883
+ ;(error as any).code = res?.errno
1884
+ reject(error)
1885
+ },
1886
+ })
1887
+ })
1910
1888
  } catch (error) {
1911
- return { data: {}, error: new AuthError(error) }
1889
+ throw error
1912
1890
  }
1891
+
1892
+ return this.createLoginState()
1913
1893
  }
1914
1894
 
1915
1895
  /**
1916
- * https://supabase.com/docs/reference/javascript/auth-getuser
1917
- * 如果存在现有会话,则获取当前用户详细信息
1918
- * @returns Promise<GetUserRes>
1896
+ * 小程序短信验证码登陆
1897
+ * @returns {Promise<LoginState>}
1898
+ * @memberof Auth
1919
1899
  */
1920
- async getUser(): Promise<GetUserRes> {
1900
+ @catchErrorsDecorator({
1901
+ title: '短信验证码登陆',
1902
+ messages: [
1903
+ '请确认以下各项:',
1904
+ ' 1 - 当前环境是否开启了小程序短信验证码登陆',
1905
+ ' 2 - 调用 auth().signInWithSms() 的语法或参数是否正确',
1906
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1907
+ ],
1908
+ })
1909
+ public async signInWithSms({
1910
+ verificationInfo = { verification_id: '', is_user: false },
1911
+ verificationCode = '',
1912
+ phoneNum = '',
1913
+ bindInfo = undefined,
1914
+ }): Promise<LoginState> {
1921
1915
  try {
1922
- const user = this.convertToUser(await this.getCurrentUser())
1923
- return { data: { user }, error: null }
1916
+ return this.signInWithUsername({
1917
+ verificationInfo,
1918
+ verificationCode,
1919
+ bindInfo,
1920
+ username: phoneNum,
1921
+ loginType: 'sms',
1922
+ })
1924
1923
  } catch (error) {
1925
- return { data: {}, error: new AuthError(error) }
1924
+ throw error
1926
1925
  }
1927
1926
  }
1928
1927
 
1929
1928
  /**
1930
- * 刷新用户信息
1931
- * @returns Promise<CommonRes>
1929
+ * 邮箱验证码登陆
1930
+ * @returns {Promise<LoginState>}
1931
+ * @memberof Auth
1932
1932
  */
1933
- async refreshUser(): Promise<CommonRes> {
1933
+ @catchErrorsDecorator({
1934
+ title: '邮箱验证码登陆',
1935
+ messages: [
1936
+ '请确认以下各项:',
1937
+ ' 1 - 当前环境是否开启了邮箱登陆',
1938
+ ' 2 - 调用 auth().signInWithEmail() 的语法或参数是否正确',
1939
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1940
+ ],
1941
+ })
1942
+ public async signInWithEmail({
1943
+ verificationInfo = { verification_id: '', is_user: false },
1944
+ verificationCode = '',
1945
+ bindInfo = undefined,
1946
+ email = '',
1947
+ }): Promise<LoginState> {
1934
1948
  try {
1935
- await this.currentUser.refresh()
1936
-
1937
- const { data: { session } = {} } = await this.getSession()
1938
- return { data: { user: session.user, session }, error: null }
1949
+ return this.signInWithUsername({
1950
+ verificationInfo,
1951
+ verificationCode,
1952
+ bindInfo,
1953
+ username: email,
1954
+ loginType: 'email',
1955
+ })
1939
1956
  } catch (error) {
1940
- return { data: {}, error: new AuthError(error) }
1957
+ throw error
1941
1958
  }
1942
1959
  }
1943
1960
 
1944
- /**
1945
- * https://supabase.com/docs/reference/javascript/auth-updateuser
1946
- * 更新用户信息
1947
- * @param params
1948
- * @returns Promise<GetUserRes | UpdateUserWithVerificationRes>
1949
- */
1950
- async updateUser(params: UpdateUserReq): Promise<GetUserRes | UpdateUserWithVerificationRes> {
1951
- try {
1952
- // 参数校验:至少有一个更新字段被提供
1953
- const hasValue = Object.keys(params).some(key => params[key] !== undefined && params[key] !== null && params[key] !== '',)
1954
- if (!hasValue) {
1955
- throw new AuthError({ message: 'At least one field must be provided for update' })
1956
- }
1957
-
1958
- const { email, phone, ...restParams } = params
1959
-
1960
- // 检查是否需要更新 email 或 phone
1961
- const needsEmailVerification = email !== undefined
1962
- const needsPhoneVerification = phone !== undefined
1963
-
1964
- let extraRes = {}
1965
-
1966
- if (needsEmailVerification || needsPhoneVerification) {
1967
- // 需要发送验证码
1968
- let verificationParams: { email?: string; phone_number?: string }
1969
- let verificationType: 'email_change' | 'phone_change'
1970
-
1971
- if (needsEmailVerification) {
1972
- verificationParams = { email: params.email }
1973
- verificationType = 'email_change'
1974
- } else {
1975
- // 正规化手机号
1976
- const formattedPhone = this.formatPhone(params.phone)
1977
- verificationParams = { phone_number: formattedPhone }
1978
- verificationType = 'phone_change'
1979
- }
1980
-
1981
- // 发送验证码
1982
- const verificationInfo = await this.getVerification(verificationParams)
1983
-
1984
- Object.keys(restParams).length > 0 && (await this.updateUserBasicInfo(restParams))
1985
-
1986
- extraRes = {
1987
- messageId: verificationInfo.verification_id,
1988
- verifyOtp: async (verifyParams: { email?: string; phone?: string; token: string }): Promise<GetUserRes> => {
1989
- try {
1990
- if (verifyParams.email && params.email === verifyParams.email) {
1991
- // 验证码验证
1992
- await this.verifyOtp({
1993
- type: 'email_change',
1994
- email: params.email,
1995
- token: verifyParams.token,
1996
- messageId: verificationInfo.verification_id,
1997
- })
1998
- await this.updateUserBasicInfo({ email: params.email })
1999
- } else if (verifyParams.phone && params.phone === verifyParams.phone) {
2000
- // 验证码验证
2001
- await this.verifyOtp({
2002
- type: 'phone_change',
2003
- phone: params.phone,
2004
- token: verifyParams.token,
2005
- messageId: verificationInfo.verification_id,
2006
- })
2007
- await this.updateUserBasicInfo({ phone: this.formatPhone(params.phone) })
2008
- } else {
2009
- await this.verifyOtp({
2010
- type: verificationType,
2011
- email: needsEmailVerification ? params.email : undefined,
2012
- phone: !needsEmailVerification ? params.phone : undefined,
2013
- token: verifyParams.token,
2014
- messageId: verificationInfo.verification_id,
2015
- })
2016
- // 验证成功后更新用户信息
2017
- await this.updateUserBasicInfo(params)
2018
- }
2019
-
2020
- const {
2021
- data: { user },
2022
- } = await this.getUser()
2023
- this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.USER_UPDATED })
1961
+ /**
1962
+ * 设置获取自定义登录 ticket 函数
1963
+ * @param {authModels.GetCustomSignTicketFn} getTickFn
1964
+ * @memberof Auth
1965
+ */
1966
+ public setCustomSignFunc(getTickFn: authModels.GetCustomSignTicketFn): void {
1967
+ this.oauthInstance.authApi.setCustomSignFunc(getTickFn)
1968
+ }
2024
1969
 
2025
- return { data: { user }, error: null }
2026
- } catch (error) {
2027
- return { data: {}, error: new AuthError(error) }
2028
- }
2029
- },
2030
- }
2031
- } else {
2032
- // 不需要验证,直接更新
2033
- await this.updateUserBasicInfo(params)
2034
- }
2035
- const {
2036
- data: { user },
2037
- } = await this.getUser()
2038
- this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.USER_UPDATED })
1970
+ /**
1971
+ *
1972
+ * @returns {Promise<LoginState>}
1973
+ * @memberof Auth
1974
+ */
1975
+ // @catchErrorsDecorator({
1976
+ // title: '自定义登录失败',
1977
+ // messages: [
1978
+ // '请确认以下各项:',
1979
+ // ' 1 - 当前环境是否开启了自定义登录',
1980
+ // ' 2 - 调用 auth().signInWithCustomTicket() 的语法或参数是否正确',
1981
+ // ' 3 - ticket 是否归属于当前环境',
1982
+ // ' 4 - 创建 ticket 的自定义登录私钥是否过期',
1983
+ // `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
1984
+ // ],
1985
+ // })
1986
+ // public async signInWithCustomTicket(): Promise<LoginState> {
1987
+ // await this.oauthInstance.authApi.signInWithCustomTicket()
1988
+ // return this.createLoginState()
1989
+ // }
2039
1990
 
2040
- return { data: { user, ...extraRes }, error: null }
2041
- } catch (error) {
2042
- return { data: {}, error: new AuthError(error) }
2043
- }
1991
+ /**
1992
+ *
1993
+ * @param {authModels.SignInRequest} params
1994
+ * @returns {Promise<LoginState>}
1995
+ * @memberof Auth
1996
+ */
1997
+ public async signIn(params: authModels.SignInRequest): Promise<LoginState> {
1998
+ await this.oauthInstance.authApi.signIn(params)
1999
+ return this.createLoginState(params)
2044
2000
  }
2045
2001
 
2002
+ // /**
2003
+ // *
2004
+ // * @param {authModels.SignUpRequest} params
2005
+ // * @returns {Promise<LoginState>}
2006
+ // * @memberof Auth
2007
+ // */
2008
+ // @catchErrorsDecorator({
2009
+ // title: '注册失败',
2010
+ // messages: [
2011
+ // '请确认以下各项:',
2012
+ // ' 1 - 当前环境是否开启了指定登录方式',
2013
+ // ' 2 - 调用 auth().signUp() 的语法或参数是否正确',
2014
+ // `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2015
+ // ],
2016
+ // })
2017
+ // public async signUp(params: authModels.SignUpRequest): Promise<any> {
2018
+ // await this.oauthInstance.authApi.signUp(params)
2019
+ // return this.createLoginState()
2020
+ // }
2021
+
2046
2022
  /**
2047
- * https://supabase.com/docs/reference/javascript/auth-getuseridentities
2048
- * 获取所有身份源
2049
- * @returns Promise<GetUserIdentitiesRes>
2023
+ * 设置密码
2024
+ * @param {authModels.SetPasswordRequest} params
2025
+ * @returns {Promise<void>}
2026
+ * @memberof Auth
2050
2027
  */
2051
- async getUserIdentities(): Promise<GetUserIdentitiesRes> {
2052
- try {
2053
- const providers = await this.oauthInstance.authApi.getProviders()
2028
+ public async setPassword(params: authModels.SetPasswordRequest): Promise<void> {
2029
+ return this.oauthInstance.authApi.setPassword(params)
2030
+ }
2054
2031
 
2055
- return {
2056
- data: {
2057
- identities: providers?.data?.filter(v => !!v.bind) as unknown as GetUserIdentitiesRes['data']['identities'],
2058
- },
2059
- error: null,
2060
- }
2061
- } catch (error) {
2062
- return { data: {}, error: new AuthError(error) }
2032
+ /**
2033
+ * 检测用户名是否已经占用
2034
+ * @param username
2035
+ */
2036
+ @catchErrorsDecorator({
2037
+ title: '获取用户是否被占用失败',
2038
+ messages: [
2039
+ '请确认以下各项:',
2040
+ ' 1 - 调用 auth().isUsernameRegistered() 的语法或参数是否正确',
2041
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2042
+ ],
2043
+ })
2044
+ public async isUsernameRegistered(username: string): Promise<boolean> {
2045
+ if (typeof username !== 'string') {
2046
+ throwError(ERRORS.INVALID_PARAMS, 'username must be a string')
2063
2047
  }
2048
+
2049
+ const { exist } = await this.oauthInstance.authApi.checkIfUserExist({ username })
2050
+ return exist
2064
2051
  }
2065
2052
 
2066
2053
  /**
2067
- * https://supabase.com/docs/reference/javascript/auth-linkidentity
2068
- * 绑定身份源到当前用户
2069
- * @param params
2070
- * @returns Promise<LinkIdentityRes>
2054
+ * 获取本地登录态-同步
2071
2055
  */
2072
- async linkIdentity(params: LinkIdentityReq): Promise<LinkIdentityRes> {
2073
- try {
2074
- // 参数校验:provider必填
2075
- this.validateParams(params, {
2076
- provider: { required: true, message: 'Provider is required' },
2077
- })
2056
+ public hasLoginState(): LoginState | null {
2057
+ if (this.cache.mode === 'async') {
2058
+ // async storage的平台调用此API提示
2059
+ printWarn(
2060
+ ERRORS.INVALID_OPERATION,
2061
+ 'current platform\'s storage is asynchronous, please use getLoginState instead',
2062
+ )
2063
+ return
2064
+ }
2078
2065
 
2079
- await this.signInWithOAuth({
2080
- provider: params.provider,
2081
- options: {
2082
- type: OAUTH_TYPE.BIND_IDENTITY,
2083
- },
2066
+ const oauthLoginState = this.oauthInstance?.authApi.hasLoginStateSync()
2067
+ if (oauthLoginState) {
2068
+ const loginState = new LoginState({
2069
+ envId: this.config.env,
2070
+ cache: this.cache,
2071
+ oauthInstance: this.oauthInstance,
2084
2072
  })
2085
-
2086
- return { data: { provider: params.provider }, error: null }
2087
- } catch (error) {
2088
- return { data: {}, error: new AuthError(error) }
2073
+ return loginState
2089
2074
  }
2075
+ return null
2090
2076
  }
2091
2077
 
2092
2078
  /**
2093
- * https://supabase.com/docs/reference/javascript/auth-unlinkidentity
2094
- * 解绑身份源
2095
- * @param params
2096
- * @returns Promise<CommonRes>
2079
+ * 获取本地登录态-异步
2080
+ * 此API为兼容异步storage的平台
2097
2081
  */
2098
- async unlinkIdentity(params: UnlinkIdentityReq): Promise<CommonRes> {
2082
+ @catchErrorsDecorator({
2083
+ title: '获取本地登录态失败',
2084
+ messages: [
2085
+ '请确认以下各项:',
2086
+ ' 1 - 调用 auth().getLoginState() 的语法或参数是否正确',
2087
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2088
+ ],
2089
+ })
2090
+ public async getLoginState() {
2091
+ let oauthLoginState = null
2092
+
2099
2093
  try {
2100
- // 参数校验:provider必填
2101
- this.validateParams(params, {
2102
- provider: { required: true, message: 'Provider is required' },
2094
+ oauthLoginState = await this.oauthInstance.authApi.getLoginState()
2095
+ } catch (error) {
2096
+ return null
2097
+ }
2098
+
2099
+ if (oauthLoginState) {
2100
+ const loginState = new LoginState({
2101
+ envId: this.config.env,
2102
+ cache: this.cache,
2103
+ oauthInstance: this.oauthInstance,
2103
2104
  })
2105
+ return loginState
2106
+ }
2104
2107
 
2105
- await this.oauthInstance.authApi.unbindProvider({ provider_id: params.provider })
2108
+ return null
2109
+ }
2106
2110
 
2107
- return { data: {}, error: null }
2108
- } catch (error) {
2109
- return { data: {}, error: new AuthError(error) }
2111
+ /**
2112
+ * @deprecated 使用 getCurrentUser 代替,因为与node-sdk方法名冲突
2113
+ * @returns
2114
+ */
2115
+ @catchErrorsDecorator({
2116
+ title: '获取用户信息失败',
2117
+ messages: [
2118
+ '请确认以下各项:',
2119
+ ' 1 - 是否已登录',
2120
+ ' 2 - 调用 auth().getUserInfo() 的语法或参数是否正确',
2121
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2122
+ ],
2123
+ })
2124
+ public async getUserInfo(): Promise<(authModels.UserInfo & Partial<User>) | null> {
2125
+ return this.getCurrentUser()
2126
+ }
2127
+
2128
+ @catchErrorsDecorator({
2129
+ title: '获取微搭插件用户信息失败',
2130
+ messages: [
2131
+ '请确认以下各项:',
2132
+ ' 1 - 是否已登录',
2133
+ ' 2 - 调用 auth().getWedaUserInfo() 的语法或参数是否正确',
2134
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2135
+ ],
2136
+ })
2137
+ public async getWedaUserInfo(): Promise<any> {
2138
+ return this.oauthInstance.authApi.getWedaUserInfo()
2139
+ }
2140
+
2141
+ public async updateUserBasicInfo(params: authModels.ModifyUserBasicInfoRequest) {
2142
+ const loginState = await this.getLoginState()
2143
+ if (loginState) {
2144
+ await (loginState.user as User).updateUserBasicInfo(params)
2110
2145
  }
2146
+ return
2111
2147
  }
2112
2148
 
2113
2149
  /**
2114
- * https://supabase.com/docs/reference/javascript/auth-reauthentication
2115
- * 重新认证
2116
- * @returns Promise<ReauthenticateRes>
2150
+ * getAuthHeader 兼容处理
2151
+ * 返回空对象
2117
2152
  */
2118
- async reauthenticate(): Promise<ReauthenticateRes> {
2119
- try {
2120
- const {
2121
- data: { user },
2122
- } = await this.getUser()
2123
-
2124
- this.validateAtLeastOne(user, [['email', 'phone']], 'You must provide either an email or phone number')
2125
- const userInfo = user.email ? { email: user.email } : { phone_number: this.formatPhone(user.phone) }
2153
+ public getAuthHeader(): {} {
2154
+ console.error('Auth.getAuthHeader API 已废弃')
2155
+ return {}
2156
+ }
2126
2157
 
2127
- // 第一步:发送验证码并存储 verificationInfo
2128
- const verificationInfo = await this.getVerification(userInfo)
2158
+ /**
2159
+ * 为已有账户绑第三方账户
2160
+ * @param {authModels.BindWithProviderRequest} params
2161
+ * @returns {Promise<void>}
2162
+ * @memberof Auth
2163
+ */
2164
+ @catchErrorsDecorator({
2165
+ title: '绑定第三方登录方式失败',
2166
+ messages: [
2167
+ '请确认以下各项:',
2168
+ ' 1 - 调用 auth().bindWithProvider() 的语法或参数是否正确',
2169
+ ' 2 - 此账户是否已经绑定此第三方',
2170
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2171
+ ],
2172
+ })
2173
+ public async bindWithProvider(params: authModels.BindWithProviderRequest): Promise<void> {
2174
+ return this.oauthInstance.authApi.bindWithProvider(params)
2175
+ }
2129
2176
 
2130
- return {
2131
- data: {
2132
- // 第二步:等待用户输入验证码(通过 Promise 包装用户输入事件)
2133
- updateUser: async (attributes: UpdateUserAttributes): Promise<SignInRes> => {
2134
- this.validateParams(attributes, {
2135
- nonce: { required: true, message: 'Nonce is required' },
2136
- })
2137
- try {
2138
- if (attributes.password) {
2139
- // 第三步:待用户输入完验证码之后,验证验证码
2140
- const verificationTokenRes = await this.verify({
2141
- verification_id: verificationInfo.verification_id,
2142
- verification_code: attributes.nonce,
2143
- })
2177
+ /**
2178
+ * 查询用户
2179
+ * @param {authModels.QueryUserProfileRequest} appended_params
2180
+ * @returns {Promise<authModels.UserProfile>}
2181
+ * @memberof Auth
2182
+ */
2183
+ public async queryUser(queryObj: authModels.QueryUserProfileRequest): Promise<authModels.QueryUserProfileResponse> {
2184
+ return this.oauthInstance.authApi.queryUserProfile(queryObj)
2185
+ }
2144
2186
 
2145
- // 第四步:获取 sudo_token
2146
- const sudoRes = await this.oauthInstance.authApi.sudo({
2147
- verification_token: verificationTokenRes.verification_token,
2148
- })
2187
+ public async getAccessToken() {
2188
+ const oauthAccessTokenRes = await this.oauthInstance.oauth2client.getAccessToken()
2189
+ return {
2190
+ accessToken: oauthAccessTokenRes,
2191
+ env: this.config.env,
2192
+ }
2193
+ }
2149
2194
 
2150
- await this.oauthInstance.authApi.setPassword({
2151
- new_password: attributes.password,
2152
- sudo_token: sudoRes.sudo_token,
2153
- })
2154
- } else {
2155
- await this.signInWithUsername({
2156
- verificationInfo,
2157
- verificationCode: attributes.nonce,
2158
- ...userInfo,
2159
- loginType: userInfo.email ? 'email' : 'phone',
2160
- })
2161
- }
2195
+ public async grantProviderToken(params: authModels.GrantProviderTokenRequest,): Promise<authModels.GrantProviderTokenResponse> {
2196
+ return this.oauthInstance.authApi.grantProviderToken(params)
2197
+ }
2162
2198
 
2163
- const { data: { session } = {} } = await this.getSession()
2199
+ public async patchProviderToken(params: authModels.PatchProviderTokenRequest,): Promise<authModels.PatchProviderTokenResponse> {
2200
+ return this.oauthInstance.authApi.patchProviderToken(params)
2201
+ }
2164
2202
 
2165
- return { data: { user: session.user, session }, error: null }
2166
- } catch (error) {
2167
- return { data: {}, error: new AuthError(error) }
2168
- }
2169
- },
2170
- },
2171
- error: null,
2172
- }
2173
- } catch (error) {
2174
- return { data: {}, error: new AuthError(error) }
2175
- }
2203
+ public async signInWithProvider(params: authModels.SignInWithProviderRequest): Promise<LoginState> {
2204
+ await this.oauthInstance.authApi.signInWithProvider(params)
2205
+ return this.createLoginState(params)
2176
2206
  }
2177
2207
 
2178
- /**
2179
- * https://supabase.com/docs/reference/javascript/auth-resend
2180
- * 重新发送验证码
2181
- * @param params
2182
- * @returns Promise<ResendRes>
2183
- */
2184
- async resend(params: ResendReq): Promise<ResendRes> {
2185
- try {
2186
- // 参数校验:email或phone必填其一
2187
- this.validateAtLeastOne(params, [['email'], ['phone']], 'You must provide either an email or phone number')
2208
+ public async signInWithWechat(params: any = {}) {
2209
+ await this.oauthInstance.authApi.signInWithWechat(params)
2210
+ return this.createLoginState(params)
2211
+ }
2188
2212
 
2189
- const target = params.type === 'signup' ? 'ANY' : 'USER'
2190
- const data: { email?: string; phone_number?: string; target: 'USER' | 'ANY' } = { target }
2191
- if ('email' in params) {
2192
- data.email = params.email
2193
- }
2213
+ public async grantToken(params: authModels.GrantTokenRequest): Promise<LoginState> {
2214
+ await this.oauthInstance.authApi.grantToken(params)
2215
+ return this.createLoginState()
2216
+ }
2194
2217
 
2195
- if ('phone' in params) {
2196
- data.phone_number = this.formatPhone(params.phone)
2197
- }
2218
+ public async genProviderRedirectUri(params: authModels.GenProviderRedirectUriRequest,): Promise<authModels.GenProviderRedirectUriResponse> {
2219
+ return this.oauthInstance.authApi.genProviderRedirectUri(params)
2220
+ }
2198
2221
 
2199
- // 重新发送验证码
2200
- const { verification_id: verificationId } = await this.oauthInstance.authApi.getVerification(data)
2222
+ public async resetPassword(params: authModels.ResetPasswordRequest): Promise<void> {
2223
+ return this.oauthInstance.authApi.resetPassword(params)
2224
+ }
2201
2225
 
2202
- return {
2203
- data: { messageId: verificationId },
2204
- error: null,
2205
- }
2206
- } catch (error: any) {
2207
- return {
2208
- data: {},
2209
- error: new AuthError(error),
2210
- }
2211
- }
2226
+ public async deviceAuthorize(params: authModels.DeviceAuthorizeRequest): Promise<authModels.DeviceAuthorizeResponse> {
2227
+ return this.oauthInstance.authApi.deviceAuthorize(params)
2212
2228
  }
2213
2229
 
2214
- /**
2215
- * https://supabase.com/docs/reference/javascript/auth-setsession
2216
- * 使用access_token和refresh_token来设置会话
2217
- * @param params
2218
- * @returns Promise<SignInRes>
2219
- */
2220
- async setSession(params: SetSessionReq): Promise<SignInRes> {
2221
- try {
2222
- this.validateParams(params, {
2223
- access_token: { required: true, message: 'Access token is required' },
2224
- refresh_token: { required: true, message: 'Refresh token is required' },
2225
- })
2230
+ public async sudo(params: authModels.SudoRequest): Promise<authModels.SudoResponse> {
2231
+ return this.oauthInstance.authApi.sudo(params)
2232
+ }
2226
2233
 
2227
- await this.oauthInstance.oauth2client.refreshToken(params, { throwError: true })
2234
+ public async deleteMe(params: authModels.WithSudoRequest): Promise<authModels.UserProfile> {
2235
+ return this.oauthInstance.authApi.deleteMe(params)
2236
+ }
2228
2237
 
2229
- const { data: { session } = {} } = await this.getSession()
2238
+ public async getProviders(): Promise<authModels.ProvidersResponse> {
2239
+ return this.oauthInstance.authApi.getProviders()
2240
+ }
2230
2241
 
2231
- this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_IN })
2242
+ public async loginScope(): Promise<string> {
2243
+ return this.oauthInstance.authApi.loginScope()
2244
+ }
2232
2245
 
2233
- return { data: { user: session.user, session }, error: null }
2234
- } catch (error) {
2235
- return { data: {}, error: new AuthError(error) }
2236
- }
2246
+ public async loginGroups(): Promise<string[]> {
2247
+ return this.oauthInstance.authApi.loginGroups()
2237
2248
  }
2238
2249
 
2239
- // https://supabase.com/docs/reference/javascript/auth-exchangecodeforsession
2240
- async exchangeCodeForSession() {
2241
- //
2250
+ public async onLoginStateChanged(callback: Function) {
2251
+ this.config.eventBus?.on(EVENTS.LOGIN_STATE_CHANGED, async (params) => {
2252
+ // getLoginState会重复触发getCredentials,导致死循环,所以getCredentials出错不再出发getLoginState
2253
+ const loginState = params?.data?.eventType !== LOGIN_STATE_CHANGED_TYPE.CREDENTIALS_ERROR ? await this.getLoginState() : {}
2254
+ callback.call(this, { ...params, ...loginState })
2255
+ })
2256
+ // 立刻执行一次回调
2257
+ const loginState = await this.getLoginState()
2258
+ callback.call(this, loginState)
2242
2259
  }
2243
2260
 
2244
2261
  /**
2245
- * 删除当前用户
2262
+ * 强制刷新token
2246
2263
  * @param params
2247
2264
  * @returns
2248
2265
  */
2249
- async deleteUser(params: DeleteMeReq): Promise<CommonRes> {
2250
- try {
2251
- this.validateParams(params, {
2252
- password: { required: true, message: 'Password is required' },
2253
- })
2266
+ public async refreshTokenForce(params: { version?: string }): Promise<Credentials> {
2267
+ return this.oauthInstance.authApi.refreshTokenForce(params)
2268
+ }
2254
2269
 
2255
- const { sudo_token } = await this.oauthInstance.authApi.sudo(params)
2270
+ /**
2271
+ * 获取身份信息
2272
+ * @returns
2273
+ */
2274
+ public async getCredentials(): Promise<Credentials> {
2275
+ return this.oauthInstance.authApi.getCredentials()
2276
+ }
2277
+ /**
2278
+ * 写入身份信息
2279
+ */
2280
+ public async setCredentials(credentials: Credentials) {
2281
+ await this.oauthInstance.oauth2client.setCredentials(credentials)
2282
+ }
2256
2283
 
2257
- await this.oauthInstance.authApi.deleteMe({ sudo_token })
2284
+ @catchErrorsDecorator({
2285
+ title: '获取身份源类型',
2286
+ messages: [
2287
+ '请确认以下各项:',
2288
+ ' 1 - 调用 auth().getProviderSubType() 的语法或参数是否正确',
2289
+ `如果问题依然存在,建议到官方问答社区提问或寻找帮助:${COMMUNITY_SITE_URL}`,
2290
+ ],
2291
+ })
2292
+ public async getProviderSubType(): Promise<authModels.ProviderSubType> {
2293
+ return this.oauthInstance.authApi.getProviderSubType()
2294
+ }
2258
2295
 
2259
- return { data: {}, error: null }
2260
- } catch (error) {
2261
- return { data: {}, error: new AuthError(error) }
2262
- }
2296
+ public async createCaptchaData(params: { state: string; redirect_uri?: string }) {
2297
+ return this.oauthInstance.authApi.createCaptchaData(params)
2263
2298
  }
2264
2299
 
2265
- /**
2266
- * 跳转系统默认登录页
2267
- * @returns {Promise<authModels.ToDefaultLoginPage>}
2268
- * @memberof Auth
2269
- */
2270
- async toDefaultLoginPage(params: authModels.ToDefaultLoginPage = {}): Promise<CommonRes> {
2271
- try {
2272
- const configVersion = params.config_version || 'env'
2273
- const query = Object.keys(params.query || {})
2274
- .map(key => `${key}=${params.query[key]}`)
2275
- .join('&')
2300
+ public async verifyCaptchaData(params: { token: string; key: string }) {
2301
+ return this.oauthInstance.authApi.verifyCaptchaData(params)
2302
+ }
2276
2303
 
2277
- if (adapterForWxMp.isMatch()) {
2278
- wx.navigateTo({ url: `/packages/$wd_system/pages/login/index${query ? `?${query}` : ''}` })
2279
- } else {
2280
- const redirectUri = params.redirect_uri || window.location.href
2281
- const urlObj = new URL(redirectUri)
2282
- const loginPage = `${urlObj.origin}/__auth/?app_id=${params.app_id || ''}&env_id=${this.config.env}&client_id=${
2283
- this.config.clientId || this.config.env
2284
- }&config_version=${configVersion}&redirect_uri=${encodeURIComponent(redirectUri)}${query ? `&${query}` : ''}`
2285
- window.location.href = loginPage
2286
- }
2287
- return { data: {}, error: null }
2288
- } catch (error) {
2289
- return { data: {}, error: new AuthError(error) }
2290
- }
2304
+ public async getMiniProgramQrCode(params: authModels.GetMiniProgramQrCodeRequest,): Promise<authModels.GetMiniProgramQrCodeResponse> {
2305
+ return this.oauthInstance.authApi.getMiniProgramCode(params)
2291
2306
  }
2292
2307
 
2293
- /**
2294
- * 自定义登录
2295
- * @param getTickFn () => Promise<string>, 获取自定义登录 ticket 的函数
2296
- * @returns
2297
- */
2298
- async signInWithCustomTicket(getTickFn?: authModels.GetCustomSignTicketFn): Promise<SignInRes> {
2299
- if (getTickFn) {
2300
- this.setCustomSignFunc(getTickFn)
2301
- }
2308
+ public async getMiniProgramQrCodeStatus(params: authModels.GetMiniProgramQrCodeStatusRequest,): Promise<authModels.GetMiniProgramQrCodeStatusResponse> {
2309
+ return this.oauthInstance.authApi.getMiniProgramQrCodeStatus(params)
2310
+ }
2302
2311
 
2303
- try {
2304
- await this.oauthInstance.authApi.signInWithCustomTicket()
2305
- const loginState = await this.createLoginState()
2312
+ public async modifyPassword(params: authModels.ModifyUserBasicInfoRequest): Promise<void> {
2313
+ return this.oauthInstance.authApi.modifyPassword(params)
2314
+ }
2306
2315
 
2307
- const { data: { session } = {} } = await this.getSession()
2316
+ public async modifyPasswordWithoutLogin(params: authModels.ModifyPasswordWithoutLoginRequest): Promise<void> {
2317
+ return this.oauthInstance.authApi.modifyPasswordWithoutLogin(params)
2318
+ }
2308
2319
 
2309
- // loginState返回是为了兼容v2版本
2310
- return { ...(loginState as any), data: { user: session.user, session }, error: null }
2311
- } catch (error) {
2312
- return { data: {}, error: new AuthError(error) }
2313
- }
2320
+ public async getUserBehaviorLog(params: authModels.GetUserBehaviorLog): Promise<authModels.GetUserBehaviorLogRes> {
2321
+ return this.oauthInstance.authApi.getUserBehaviorLog(params)
2314
2322
  }
2315
2323
 
2316
2324
  /**
2317
- * 小程序openId静默登录
2318
- * @param params
2319
- * @returns Promise<SignInRes>
2325
+ * sms/email 验证码登录/注册,逻辑一致收敛
2320
2326
  */
2321
- async signInWithOpenId({ useWxCloud = true } = {}): Promise<SignInRes> {
2322
- if (!adapterForWxMp.isMatch()) {
2323
- throw Error('wx api undefined')
2324
- }
2325
- const wxInfo = wx.getAccountInfoSync().miniProgram
2326
-
2327
- const mainFunc = async (code) => {
2328
- let result: authModels.GrantProviderTokenResponse | undefined = undefined
2329
- let credentials: Credentials | undefined = undefined
2327
+ public async signInWithUsername({
2328
+ verificationInfo = { verification_id: '', is_user: false },
2329
+ verificationCode = '',
2330
+ username: rawUsername = '',
2331
+ bindInfo = undefined,
2332
+ loginType = '',
2333
+ }: {
2334
+ verificationInfo?: authModels.GetVerificationResponse
2335
+ verificationCode?: string
2336
+ username?: string
2337
+ bindInfo?: any
2338
+ loginType?: string
2339
+ }): Promise<LoginState> {
2340
+ try {
2341
+ // 1. 验证验证码
2342
+ const verifyRes = await this.oauthInstance.authApi.verify({
2343
+ verification_id: verificationInfo.verification_id,
2344
+ verification_code: verificationCode,
2345
+ })
2330
2346
 
2331
- try {
2332
- result = await this.oauthInstance.authApi.grantProviderToken(
2333
- {
2334
- provider_id: wxInfo?.appId,
2335
- provider_code: code,
2336
- provider_params: {
2337
- provider_code_type: 'open_id',
2338
- appid: wxInfo?.appId,
2339
- },
2340
- },
2341
- useWxCloud,
2342
- )
2347
+ if ((verifyRes as any)?.error_code) {
2348
+ throw verifyRes
2349
+ }
2343
2350
 
2344
- if ((result as any)?.error_code || !result.provider_token) {
2345
- throw result
2346
- }
2351
+ // eslint-disable-next-line @typescript-eslint/naming-convention
2352
+ const { verification_token } = verifyRes
2347
2353
 
2348
- credentials = await this.oauthInstance.authApi.signInWithProvider(
2349
- { provider_token: result.provider_token },
2350
- useWxCloud,
2351
- )
2354
+ // 手机登录参数
2355
+ let username = this.formatPhone(rawUsername)
2356
+ let signUpParam: any = { phone_number: username }
2352
2357
 
2353
- if ((credentials as any)?.error_code) {
2354
- throw credentials
2355
- }
2356
- } catch (error) {
2357
- throw error
2358
+ // 邮箱登录参数
2359
+ if (loginType === 'email') {
2360
+ username = rawUsername
2361
+ signUpParam = { email: username }
2358
2362
  }
2359
- await this.oauthInstance.oauth2client.setCredentials(credentials as Credentials)
2360
- }
2361
2363
 
2362
- try {
2363
- await new Promise((resolve, reject) => {
2364
- wx.login({
2365
- success: async (res: { code: string }) => {
2366
- try {
2367
- await mainFunc(res.code)
2368
- resolve(true)
2369
- } catch (error) {
2370
- reject(error)
2371
- }
2372
- },
2373
- fail: (res: any) => {
2374
- const error = new Error(res?.errMsg)
2375
- ;(error as any).code = res?.errno
2376
- reject(error)
2377
- },
2364
+ // 2. 根据是否已经是用户,分别走登录或注册逻辑
2365
+ if (verificationInfo.is_user) {
2366
+ // 私有化环境或者自定义应用走v1版本的老逻辑
2367
+ const signInRes = await this.oauthInstance.authApi.signIn({
2368
+ username,
2369
+ verification_token,
2378
2370
  })
2379
- })
2380
2371
 
2381
- const loginState = await this.createLoginState()
2372
+ if ((signInRes as any)?.error_code) {
2373
+ throw signInRes
2374
+ }
2382
2375
 
2383
- const { data: { session } = {} } = await this.getSession()
2376
+ if (bindInfo) {
2377
+ const bindRes = await this.oauthInstance.authApi.bindWithProvider({
2378
+ provider_token: (bindInfo as any)?.providerToken,
2379
+ })
2384
2380
 
2385
- // loginState返回是为了兼容v2版本
2386
- return { ...(loginState as any), data: { user: session.user, session }, error: null }
2381
+ if ((bindRes as any)?.error_code) {
2382
+ throw bindRes
2383
+ }
2384
+ }
2385
+ } else {
2386
+ // 自定义应用走signUp逻辑
2387
+ const signUpRes = await this.oauthInstance.authApi.signUp({
2388
+ ...signUpParam,
2389
+ verification_token,
2390
+ provider_token: (bindInfo as any)?.providerId,
2391
+ })
2392
+
2393
+ if ((signUpRes as any)?.error_code) {
2394
+ throw signUpRes
2395
+ }
2396
+ }
2397
+
2398
+ return this.createLoginState()
2387
2399
  } catch (error) {
2388
- return { data: {}, error: new AuthError(error) }
2400
+ throw error
2389
2401
  }
2390
2402
  }
2391
2403
 
2392
- /**
2393
- * 小程序手机号授权登录
2394
- * @param params
2395
- * @returns Promise<SignInRes>
2396
- */
2397
- // async signInWithPhoneAuth({ phoneCode = '', useWxCloud = false }): Promise<SignInRes> {
2398
- async signInWithPhoneAuth({ phoneCode = '' }): Promise<SignInRes> {
2399
- // if (!adapterForWxMp.isMatch()) {
2400
- // return { data: {}, error: new AuthError({ message: 'wx api undefined' }) }
2401
- // }
2402
- // const wxInfo = wx.getAccountInfoSync().miniProgram
2403
- // const providerInfo = {
2404
- // provider_params: { provider_code_type: 'phone', code: phoneCode, appid: wxInfo.appId },
2405
- // provider_id: wxInfo.appId,
2406
- // }
2407
-
2408
- // const { code } = await wx.login()
2409
- // ;(providerInfo as any).provider_code = code
2410
-
2411
- // try {
2412
- // const providerToken = await this.oauthInstance.authApi.grantProviderToken(providerInfo, useWxCloud)
2413
- // if (providerToken.error_code) {
2414
- // throw providerToken
2415
- // }
2404
+ async createLoginState(
2405
+ params?: { version?: string; query?: any },
2406
+ options?: { asyncRefreshUser?: boolean; userInfo?: any },
2407
+ ): Promise<LoginState> {
2408
+ const loginState = new LoginState({
2409
+ envId: this.config.env,
2410
+ cache: this.cache,
2411
+ oauthInstance: this.oauthInstance,
2412
+ })
2416
2413
 
2417
- // const signInRes = await this.oauthInstance.authApi.signInWithProvider({
2418
- // provider_token: providerToken.provider_token,
2419
- // }, useWxCloud)
2414
+ await loginState.checkLocalStateAsync()
2420
2415
 
2421
- // if ((signInRes as any)?.error_code) {
2422
- // throw signInRes
2423
- // }
2424
- // } catch (error) {
2425
- // return { data: {}, error: new AuthError(error) }
2426
- // }
2416
+ if (options?.userInfo) {
2417
+ loginState.user.setLocalUserInfo(options.userInfo)
2418
+ } else {
2419
+ if (options?.asyncRefreshUser) {
2420
+ loginState.user.refresh(params)
2421
+ } else {
2422
+ await loginState.user.refresh(params)
2423
+ }
2424
+ }
2427
2425
 
2428
- // const loginState = await this.createLoginState()
2426
+ this.config.eventBus?.fire(EVENTS.LOGIN_STATE_CHANGED, { eventType: LOGIN_STATE_CHANGED_TYPE.SIGN_IN })
2429
2427
 
2430
- // const { data: { session } = {} } = await this.getSession()
2428
+ this.config.eventBus?.fire(EVENTS.AUTH_STATE_CHANGED, { event: AUTH_STATE_CHANGED_TYPE.SIGNED_IN })
2429
+ return loginState
2430
+ }
2431
2431
 
2432
- // // loginState返回是为了兼容v2版本
2433
- // return { ...(loginState as any), data: { user: session.user, session }, error: null }
2434
- if (!adapterForWxMp.isMatch()) {
2435
- return { data: {}, error: new AuthError({ message: 'wx api undefined' }) }
2436
- }
2437
- const wxInfo = wx.getAccountInfoSync().miniProgram
2438
- const providerInfo = {
2439
- provider_params: { provider_code_type: 'phone' },
2440
- provider_id: wxInfo.appId,
2432
+ async setAccessKey() {
2433
+ let accessToken = ''
2434
+ let scope = ''
2435
+ if (this.config.accessKey) {
2436
+ accessToken = this.config.accessKey
2437
+ scope = 'accessKey'
2438
+ } else if (this.config.auth?.secretId && this.config.auth?.secretKey) {
2439
+ accessToken = ''
2440
+ scope = DEFAULT_NODE_ACCESS_SCOPE
2441
2441
  }
2442
2442
 
2443
- const { code } = await wx.login()
2444
- ;(providerInfo as any).provider_code = code
2443
+ if (!scope) {
2444
+ return
2445
+ }
2445
2446
 
2446
2447
  try {
2447
- let providerToken = await this.oauthInstance.authApi.grantProviderToken(providerInfo)
2448
- if (providerToken.error_code) {
2449
- throw providerToken
2450
- }
2451
-
2452
- providerToken = await this.oauthInstance.authApi.patchProviderToken({
2453
- provider_token: providerToken.provider_token,
2454
- provider_id: wxInfo.appId,
2455
- provider_params: {
2456
- code: phoneCode,
2457
- provider_code_type: 'phone',
2458
- },
2459
- })
2460
- if (providerToken.error_code) {
2461
- throw providerToken
2462
- }
2463
-
2464
- const signInRes = await this.oauthInstance.authApi.signInWithProvider({
2465
- provider_token: providerToken.provider_token,
2448
+ this.oauthInstance.oauth2client.setAccessKeyCredentials({
2449
+ access_token: accessToken,
2450
+ token_type: 'Bearer',
2451
+ scope,
2452
+ expires_at: new Date(+new Date() + +new Date()),
2453
+ expires_in: +new Date() + +new Date(),
2466
2454
  })
2467
-
2468
- if ((signInRes as any)?.error_code) {
2469
- throw signInRes
2470
- }
2471
2455
  } catch (error) {
2472
- return { data: {}, error: new AuthError(error) }
2456
+ console.warn('accessKey error: ', error)
2473
2457
  }
2474
-
2475
- const loginState = await this.createLoginState()
2476
-
2477
- const { data: { session } = {} } = await this.getSession()
2478
-
2479
- // loginState返回是为了兼容v2版本
2480
- return { ...(loginState as any), data: { user: session.user, session }, error: null }
2481
2458
  }
2482
2459
 
2483
2460
  protected formatPhone(phone: string) {
@@ -2742,13 +2719,12 @@ function levenshteinDistance(a: string, b: string): number {
2742
2719
  function findSimilarMethod(target: string, methods: string[]): string | null {
2743
2720
  let bestMatch: string | null = null
2744
2721
  let bestDistance = Infinity
2745
- const maxDistance = Math.max(3, Math.floor(target.length * 0.4)) // 允许 40% 的编辑距离
2746
2722
 
2747
2723
  for (const method of methods) {
2748
2724
  // 跳过私有方法和构造函数
2749
2725
  if (method.startsWith('_') || method === 'constructor') continue
2750
2726
  const distance = levenshteinDistance(target, method)
2751
- if (distance < bestDistance && distance <= maxDistance) {
2727
+ if (distance < bestDistance) {
2752
2728
  bestDistance = distance
2753
2729
  bestMatch = method
2754
2730
  }