@cloudbase/oauth 2.23.3 → 2.24.0

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.
Files changed (43) hide show
  1. package/dist/cjs/auth/apis.js +170 -14
  2. package/dist/cjs/auth/auth-error.js +32 -0
  3. package/dist/cjs/auth/consts.js +24 -2
  4. package/dist/cjs/auth/models.js +1 -1
  5. package/dist/cjs/captcha/captcha-dom.js +223 -0
  6. package/dist/cjs/captcha/captcha.js +11 -102
  7. package/dist/cjs/index.js +25 -3
  8. package/dist/cjs/oauth2client/interface.js +1 -1
  9. package/dist/cjs/oauth2client/models.js +1 -1
  10. package/dist/cjs/oauth2client/oauth2client.js +384 -110
  11. package/dist/cjs/utils/base64.js +15 -2
  12. package/dist/esm/auth/apis.js +113 -2
  13. package/dist/esm/auth/auth-error.js +9 -0
  14. package/dist/esm/auth/consts.js +22 -0
  15. package/dist/esm/captcha/captcha-dom.js +129 -0
  16. package/dist/esm/captcha/captcha.js +14 -97
  17. package/dist/esm/index.js +18 -2
  18. package/dist/esm/oauth2client/oauth2client.js +162 -36
  19. package/dist/esm/utils/base64.js +12 -0
  20. package/dist/miniprogram/index.js +1 -1
  21. package/dist/types/auth/apis.d.ts +19 -1
  22. package/dist/types/auth/auth-error.d.ts +6 -0
  23. package/dist/types/auth/consts.d.ts +22 -0
  24. package/dist/types/auth/models.d.ts +2 -0
  25. package/dist/types/captcha/captcha-dom.d.ts +3 -0
  26. package/dist/types/captcha/captcha.d.ts +3 -1
  27. package/dist/types/index.d.ts +11 -2
  28. package/dist/types/oauth2client/interface.d.ts +1 -1
  29. package/dist/types/oauth2client/models.d.ts +14 -0
  30. package/dist/types/oauth2client/oauth2client.d.ts +58 -2
  31. package/dist/types/utils/base64.d.ts +5 -0
  32. package/package.json +4 -4
  33. package/src/auth/apis.ts +189 -4
  34. package/src/auth/auth-error.ts +21 -0
  35. package/src/auth/consts.ts +28 -0
  36. package/src/auth/models.ts +2 -0
  37. package/src/captcha/captcha-dom.ts +178 -0
  38. package/src/captcha/captcha.ts +25 -115
  39. package/src/index.ts +51 -3
  40. package/src/oauth2client/interface.ts +1 -1
  41. package/src/oauth2client/models.ts +28 -0
  42. package/src/oauth2client/oauth2client.ts +254 -34
  43. package/src/utils/base64.ts +12 -0
@@ -28,6 +28,9 @@ export interface AuthOptions {
28
28
  };
29
29
  i18n?: ICloudbaseConfig['i18n'];
30
30
  useWxCloud?: boolean;
31
+ eventBus?: any;
32
+ detectSessionInUrl?: boolean;
33
+ debug?: boolean;
31
34
  }
32
35
  export declare class Auth {
33
36
  private static parseParamsToSearch;
@@ -54,15 +57,29 @@ export declare class Auth {
54
57
  grantProviderToken(params: GrantProviderTokenRequest, useWxCloud?: boolean): Promise<GrantProviderTokenResponse>;
55
58
  patchProviderToken(params: PatchProviderTokenRequest): Promise<PatchProviderTokenResponse>;
56
59
  signInWithProvider(params: SignInWithProviderRequest, useWxCloud?: boolean): Promise<Credentials>;
60
+ toBindIdentity(params: {
61
+ provider_token: string;
62
+ provider: string;
63
+ credentials?: Credentials;
64
+ fireEvent?: boolean;
65
+ }): Promise<any>;
66
+ getInitialSession(): Promise<{
67
+ data: {
68
+ session: Credentials;
69
+ user?: any;
70
+ } | null;
71
+ error: Error | null;
72
+ }>;
57
73
  signInCustom(params: SignInCustomRequest): Promise<Credentials>;
58
74
  signInWithWechat(params?: any): Promise<Credentials>;
59
- bindWithProvider(params: BindWithProviderRequest): Promise<void>;
75
+ bindWithProvider(params: BindWithProviderRequest, credentials?: Credentials): Promise<void>;
60
76
  getUserProfile(params: {
61
77
  version?: string;
62
78
  }): Promise<UserProfile>;
63
79
  getUserInfo(params?: {
64
80
  version?: string;
65
81
  query?: string;
82
+ credentials?: Credentials;
66
83
  }): Promise<UserInfo>;
67
84
  getWedaUserInfo(): Promise<any>;
68
85
  deleteMe(params: WithSudoRequest): Promise<UserProfile>;
@@ -120,4 +137,5 @@ export declare class Auth {
120
137
  getUserBehaviorLog(params: GetUserBehaviorLog): Promise<GetUserBehaviorLogRes>;
121
138
  modifyPassword(params: ModifyUserBasicInfoRequest): Promise<void>;
122
139
  modifyPasswordWithoutLogin(params: ModifyPasswordWithoutLoginRequest): Promise<void>;
140
+ private restoreUrlState;
123
141
  }
@@ -0,0 +1,6 @@
1
+ export declare class AuthError extends Error {
2
+ code: (string & {}) | undefined;
3
+ status: number | undefined;
4
+ protected __isAuthError: boolean;
5
+ constructor(error: any);
6
+ }
@@ -108,3 +108,25 @@ export declare enum ErrorType {
108
108
  CAPTCHA_REQUIRED = "captcha_required",
109
109
  CAPTCHA_INVALID = "captcha_invalid"
110
110
  }
111
+ export declare const LOGIN_STATE_CHANGED_TYPE: {
112
+ SIGN_OUT: string;
113
+ SIGN_IN: string;
114
+ CREDENTIALS_ERROR: string;
115
+ };
116
+ export declare const AUTH_STATE_CHANGED_TYPE: {
117
+ SIGNED_OUT: string;
118
+ SIGNED_IN: string;
119
+ INITIAL_SESSION: string;
120
+ PASSWORD_RECOVERY: string;
121
+ TOKEN_REFRESHED: string;
122
+ USER_UPDATED: string;
123
+ BIND_IDENTITY: string;
124
+ };
125
+ export declare const EVENTS: {
126
+ LOGIN_STATE_CHANGED: string;
127
+ AUTH_STATE_CHANGED: string;
128
+ };
129
+ export declare const OAUTH_TYPE: {
130
+ SIGN_IN: string;
131
+ BIND_IDENTITY: string;
132
+ };
@@ -511,6 +511,8 @@ export interface ModifyUserBasicInfoRequest {
511
511
  gender?: 'MALE' | 'FEMALE';
512
512
  password?: string;
513
513
  new_password?: string;
514
+ email?: string;
515
+ phone?: string;
514
516
  }
515
517
  export interface GetUserBehaviorLog {
516
518
  type: 'LOGIN' | 'MODIFY';
@@ -0,0 +1,3 @@
1
+ import { CaptchaToken } from '@cloudbase/adapter-interface';
2
+ import { Auth } from '../auth/apis';
3
+ export declare const openURIWithCallback: (url: string, oauthInstance?: Auth) => Promise<CaptchaToken>;
@@ -1,7 +1,9 @@
1
1
  import { SimpleStorage, RequestFunction } from '../oauth2client/interface';
2
2
  import { AuthClientRequestOptions } from '../oauth2client/models';
3
3
  import { SDKAdapterInterface } from '@cloudbase/adapter-interface';
4
+ import { Auth } from '../auth/apis';
4
5
  export interface CaptchaOptions {
6
+ env: string;
5
7
  clientId: string;
6
8
  request: RequestFunction;
7
9
  storage: SimpleStorage;
@@ -9,6 +11,7 @@ export interface CaptchaOptions {
9
11
  adapter?: SDKAdapterInterface & {
10
12
  isMatch?: () => boolean;
11
13
  };
14
+ oauthInstance?: Auth;
12
15
  }
13
16
  type OpenURIWithCallbackFuction = (url: string) => Promise<CaptchaToken>;
14
17
  export interface CaptchaToken {
@@ -31,7 +34,6 @@ export declare class Captcha {
31
34
  isMatch(): boolean;
32
35
  request<T>(url: string, options?: CaptchaRequestOptions): Promise<T>;
33
36
  private getDefaultOpenURIWithCallback;
34
- private defaultOpenURIWithCallback;
35
37
  private getCaptchaToken;
36
38
  private appendCaptchaTokenToURL;
37
39
  private saveCaptchaToken;
@@ -1,13 +1,22 @@
1
1
  import { OAuth2Client } from './oauth2client/oauth2client';
2
2
  import { AuthOptions, Auth } from './auth/apis';
3
+ import { Credentials } from './oauth2client/models';
3
4
  export { Auth } from './auth/apis';
4
- export { AUTH_API_PREFIX } from './auth/consts';
5
+ export { AUTH_API_PREFIX, OAUTH_TYPE } from './auth/consts';
6
+ export { AuthError } from './auth/auth-error';
5
7
  export * as authModels from './auth/models';
6
- export type { ProviderProfile } from './auth/models';
8
+ export type { ProviderProfile, UserInfo, ModifyUserBasicInfoRequest } from './auth/models';
7
9
  export type { Credentials, OAuth2ClientOptions, ResponseError, AuthClientRequestOptions } from './oauth2client/models';
8
10
  export type { AuthOptions } from './auth/apis';
11
+ export { weappJwtDecodeAll } from './utils/base64';
12
+ export { LOGIN_STATE_CHANGED_TYPE, EVENTS, AUTH_STATE_CHANGED_TYPE } from './auth/consts';
9
13
  export declare class CloudbaseOAuth {
10
14
  oauth2client: OAuth2Client;
11
15
  authApi: Auth;
16
+ private detectSessionInUrl;
12
17
  constructor(authOptions: AuthOptions);
18
+ initializeSession(onUserObtained?: (data: {
19
+ session: Credentials;
20
+ user?: any;
21
+ }) => void | Promise<void>): void;
13
22
  }
@@ -1,7 +1,7 @@
1
1
  import { Credentials, AuthClientRequestOptions } from './models';
2
2
  export declare abstract class AuthClient {
3
3
  abstract request: RequestFunction;
4
- abstract setCredentials(credentials?: Credentials): void;
4
+ abstract setCredentials(credentials?: Credentials): Promise<void>;
5
5
  abstract getAccessToken(): Promise<string>;
6
6
  }
7
7
  export type RequestFunction = <T>(url: string, options?: AuthClientRequestOptions) => Promise<T>;
@@ -35,6 +35,7 @@ export interface AuthClientRequestOptions extends RequestOptions {
35
35
  withBasicAuth?: boolean;
36
36
  retry?: number;
37
37
  useWxCloud?: boolean;
38
+ getCredentials?: () => Credentials | Promise<Credentials | null> | null;
38
39
  [key: string]: any;
39
40
  }
40
41
  export interface OAuth2ClientOptions {
@@ -57,4 +58,17 @@ export interface OAuth2ClientOptions {
57
58
  onCredentialsError?: AuthOptions['onCredentialsError'];
58
59
  i18n?: ICloudbaseConfig['i18n'];
59
60
  useWxCloud?: boolean;
61
+ eventBus?: any;
62
+ debug?: boolean;
63
+ getInitialSession?: () => Promise<{
64
+ data: {
65
+ session: Credentials;
66
+ user?: any;
67
+ } | null;
68
+ error: Error | null;
69
+ }>;
70
+ onInitialSessionObtained?: (data: {
71
+ session: Credentials;
72
+ user?: any;
73
+ }) => void | Promise<void>;
60
74
  }
@@ -23,11 +23,45 @@ declare class DefaultStorage implements SimpleStorage {
23
23
  setItemSync(key: string, value: string): void;
24
24
  }
25
25
  export declare const defaultStorage: DefaultStorage;
26
+ interface LocalCredentialsOptions {
27
+ tokenSectionName: string;
28
+ storage: SimpleStorage;
29
+ clientId: string;
30
+ credentials?: Credentials;
31
+ }
32
+ declare class LocalCredentials {
33
+ private tokenSectionName;
34
+ private storage;
35
+ private clientId;
36
+ private credentials;
37
+ private accessKeyCredentials;
38
+ private singlePromise;
39
+ constructor(options: LocalCredentialsOptions);
40
+ getStorageCredentialsSync(): Credentials | null;
41
+ setCredentials(credentials?: Credentials): Promise<void>;
42
+ setAccessKeyCredentials(credentials?: Credentials): void;
43
+ getCredentials(): Promise<Credentials | null>;
44
+ private getStorageCredentials;
45
+ }
26
46
  export declare class OAuth2Client implements AuthClient {
27
47
  private static defaultRetry;
28
48
  private static minRetry;
29
49
  private static maxRetry;
30
50
  private static retryInterval;
51
+ localCredentials: LocalCredentials;
52
+ initializePromise: Promise<{
53
+ error: Error | null;
54
+ }> | null;
55
+ protected lockAcquired: boolean;
56
+ protected pendingInLock: Promise<any>[];
57
+ protected logDebugMessages: boolean;
58
+ protected getInitialSession?: () => Promise<{
59
+ data: {
60
+ session: Credentials;
61
+ user?: any;
62
+ } | null;
63
+ error: Error | null;
64
+ }>;
31
65
  private apiOrigin;
32
66
  private apiPath;
33
67
  private clientId;
@@ -35,7 +69,6 @@ export declare class OAuth2Client implements AuthClient {
35
69
  private retry;
36
70
  private clientSecret?;
37
71
  private baseRequest;
38
- private localCredentials;
39
72
  private storage;
40
73
  private deviceID?;
41
74
  private tokenInURL?;
@@ -45,9 +78,25 @@ export declare class OAuth2Client implements AuthClient {
45
78
  private anonymousSignInFunc;
46
79
  private wxCloud;
47
80
  private useWxCloud;
81
+ private eventBus;
48
82
  private basicAuth;
49
83
  private onCredentialsError;
84
+ private onInitialSessionObtained?;
50
85
  constructor(options: OAuth2ClientOptions);
86
+ setGetInitialSession(callback: () => Promise<{
87
+ data: {
88
+ session: Credentials;
89
+ user?: any;
90
+ } | null;
91
+ error: Error | null;
92
+ }>): void;
93
+ setOnInitialSessionObtained(callback: (data: {
94
+ session: Credentials;
95
+ user?: any;
96
+ }) => void | Promise<void>): void;
97
+ initialize(): Promise<{
98
+ error: Error | null;
99
+ }>;
51
100
  setCredentials(credentials?: Credentials): Promise<void>;
52
101
  setAccessKeyCredentials(credentials?: Credentials): void;
53
102
  getAccessToken(): Promise<string>;
@@ -58,7 +107,10 @@ export declare class OAuth2Client implements AuthClient {
58
107
  getCredentialsAsync(): Promise<Credentials | null>;
59
108
  getScope(): Promise<string>;
60
109
  getGroups(): Promise<string[]>;
61
- refreshToken(credentials: Credentials): Promise<Credentials>;
110
+ refreshToken(credentials: Credentials, options?: {
111
+ throwError?: boolean;
112
+ }): Promise<Credentials>;
113
+ private _refreshToken;
62
114
  private anonymousLogin;
63
115
  private checkRetry;
64
116
  private formatRetry;
@@ -67,5 +119,9 @@ export declare class OAuth2Client implements AuthClient {
67
119
  private defaultRefreshTokenFunc;
68
120
  private getDeviceId;
69
121
  private unAuthenticatedError;
122
+ private _debug;
123
+ private _initialize;
124
+ private _acquireLock;
125
+ private _getCredentials;
70
126
  }
71
127
  export {};
@@ -2,3 +2,8 @@ export declare function weBtoa(string: string): string;
2
2
  export declare const weAtob: (string: string) => string;
3
3
  export declare function base64_url_decode(str: string): string;
4
4
  export declare function weappJwtDecode(token: string, options?: any): any;
5
+ export declare function weappJwtDecodeAll(token: string): {
6
+ claims: any;
7
+ header: any;
8
+ signature: any;
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/oauth",
3
- "version": "2.23.3",
3
+ "version": "2.24.0",
4
4
  "description": "cloudbase javascript sdk auth componets",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -19,16 +19,16 @@
19
19
  "precommit": "npm run lint"
20
20
  },
21
21
  "dependencies": {
22
- "@cloudbase/utilities": "2.23.3"
22
+ "@cloudbase/utilities": "2.24.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@babel/preset-env": "^7.25.3",
26
- "@cloudbase/types": "2.23.3",
26
+ "@cloudbase/types": "2.24.0",
27
27
  "@types/node": "^22.5.4",
28
28
  "terser-webpack-plugin": "^3.0.2",
29
29
  "ts-loader": "^9.5.1",
30
30
  "webpack-bundle-analyzer": "^4.9.1"
31
31
  },
32
32
  "license": "Apache-2.0",
33
- "gitHead": "d5f566dbd448be5d75127c4a506bcaf0ff3ae84b"
33
+ "gitHead": "7dac230344ecae3eec15358b2f85d782fde51839"
34
34
  }
package/src/auth/apis.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- import { ApiUrls, ApiUrlsV2, ErrorType } from './consts'
3
+ import { ApiUrls, ApiUrlsV2, AUTH_STATE_CHANGED_TYPE, ErrorType, EVENTS, OAUTH_TYPE } from './consts'
4
4
  import {
5
5
  GetVerificationRequest,
6
6
  GetVerificationResponse,
@@ -69,6 +69,7 @@ import { deepClone } from '../utils'
69
69
  import MyURLSearchParams from '../utils/urlSearchParams'
70
70
  import { SDKAdapterInterface } from '@cloudbase/adapter-interface'
71
71
  import { ICloudbaseConfig } from '@cloudbase/types'
72
+ import { AuthError } from '..'
72
73
 
73
74
  function getEncryptUtils(isEncrypt, adapter: SDKAdapterInterface) {
74
75
  const getUtils = () => {
@@ -119,6 +120,16 @@ export interface AuthOptions {
119
120
  headers?: { [key: string]: string }
120
121
  i18n?: ICloudbaseConfig['i18n']
121
122
  useWxCloud?: boolean
123
+ eventBus?: any
124
+ /**
125
+ * Set to true if you want to automatically detect OAuth grants in the URL
126
+ * and exchange the code for credentials.
127
+ */
128
+ detectSessionInUrl?: boolean
129
+ /**
130
+ * Enable debug logging
131
+ */
132
+ debug?: boolean
122
133
  }
123
134
 
124
135
  /**
@@ -158,16 +169,19 @@ export class Auth {
158
169
  onCredentialsError: opts.onCredentialsError,
159
170
  headers: opts.headers || {},
160
171
  i18n: opts.i18n,
172
+ debug: opts.debug,
161
173
  }
162
174
  oAuth2Client = new OAuth2Client(initOptions)
163
175
  }
164
176
  if (!request) {
165
177
  const baseRequest = oAuth2Client.request.bind(oAuth2Client)
166
178
  const captcha = new Captcha({
179
+ env: opts.env,
167
180
  clientId: opts.clientId,
168
181
  request: baseRequest,
169
182
  storage: opts.storage,
170
183
  adapter: opts.adapter,
184
+ oauthInstance: this,
171
185
  ...opts.captchaOptions,
172
186
  })
173
187
  request = captcha.request.bind(captcha)
@@ -443,6 +457,154 @@ export class Auth {
443
457
  return Promise.resolve(credentials)
444
458
  }
445
459
 
460
+ public async toBindIdentity(params: {
461
+ provider_token: string
462
+ provider: string
463
+ credentials?: Credentials
464
+ fireEvent?: boolean
465
+ }) {
466
+ const credentials = params.credentials || (await this.config.credentialsClient.localCredentials.getCredentials())
467
+ let res: any
468
+ try {
469
+ await this.bindWithProvider(
470
+ {
471
+ provider_token: params.provider_token,
472
+ },
473
+ credentials,
474
+ )
475
+ res = { data: { type: OAUTH_TYPE.BIND_IDENTITY, provider: params.provider }, error: null }
476
+ } catch (error) {
477
+ res = { data: { type: OAUTH_TYPE.BIND_IDENTITY }, error: new AuthError(error) }
478
+ }
479
+
480
+ if (params.fireEvent) {
481
+ this.config.eventBus?.fire?.(EVENTS.AUTH_STATE_CHANGED, {
482
+ event: AUTH_STATE_CHANGED_TYPE.BIND_IDENTITY,
483
+ info: res,
484
+ })
485
+ }
486
+
487
+ return res
488
+ }
489
+
490
+ /**
491
+ * 获取初始 session(如从 URL 中的 OAuth 回调)。
492
+ * 用于 OAuth2Client.initialize() 回调。
493
+ * 内部处理 URL 检测、OAuth 验证,返回 credentials 和 user。
494
+ * 不调用 setCredentials,由 OAuth2Client 负责保存。
495
+ *
496
+ * @returns { data: { session: Credentials; user?: any } | null, error: Error | null }
497
+ */
498
+ public async getInitialSession(): Promise<{
499
+ data: { session: Credentials; user?: any } | null
500
+ error: Error | null
501
+ }> {
502
+ let data: any = {}
503
+ try {
504
+ // Check if running in browser
505
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
506
+ return { data: null, error: null }
507
+ }
508
+
509
+ // Parse URL parameters
510
+ const localSearch = new URLSearchParams(location?.search)
511
+ const code = localSearch.get('code')
512
+ const state = localSearch.get('state')
513
+
514
+ // No OAuth callback detected
515
+ if (!code || !state) {
516
+ return { data: null, error: null }
517
+ }
518
+
519
+ // Get provider from sessionStorage (saved during signInWithOAuth)
520
+ let cacheData: {
521
+ provider?: string
522
+ search?: string
523
+ hash?: string
524
+ type?: (typeof OAUTH_TYPE)[keyof typeof OAUTH_TYPE]
525
+ } | null = null
526
+ try {
527
+ cacheData = JSON.parse(sessionStorage.getItem(state) || 'null')
528
+ } catch {
529
+ // ignore
530
+ }
531
+ data = { type: cacheData?.type }
532
+ // Check for error in URL
533
+ const errorParam = localSearch.get('error')
534
+ const errorDescription = localSearch.get('error_description')
535
+ if (errorParam || errorDescription) {
536
+ return {
537
+ data,
538
+ error: new AuthError({ message: errorDescription || errorParam || 'Unknown error from OAuth provider' }),
539
+ }
540
+ }
541
+
542
+ const provider = cacheData?.provider || localSearch.get('provider')
543
+
544
+ if (!provider) {
545
+ return { data, error: new AuthError({ message: 'Provider is required for OAuth verification' }) }
546
+ }
547
+
548
+ // 获取当前页面的 redirect_uri
549
+ const redirectUri = location.origin + location.pathname
550
+
551
+ // Step 1: 获取 provider token
552
+ const { provider_token: providerToken } = await this.grantProviderToken({
553
+ provider_id: provider,
554
+ provider_redirect_uri: redirectUri,
555
+ provider_code: code,
556
+ })
557
+
558
+ let credentials: Credentials
559
+ let user: any = null
560
+ let res: any
561
+
562
+ if (cacheData.type === OAUTH_TYPE.BIND_IDENTITY) {
563
+ credentials = await this.config.credentialsClient.localCredentials.getCredentials()
564
+ res = await this.toBindIdentity({ provider, provider_token: providerToken, credentials })
565
+ } else if (cacheData.type === OAUTH_TYPE.SIGN_IN) {
566
+ res = this.getParamsByVersion({ provider }, 'AUTH_SIGN_IN_WITH_PROVIDER_URL')
567
+
568
+ // Step 2: 用 provider token 换取 credentials(直接调用 API,不触发 setCredentials)
569
+ credentials = await this.config.request<Credentials>(res.url, {
570
+ method: 'POST',
571
+ body: { provider_token: providerToken },
572
+ })
573
+
574
+ // Step 3: 获取 user info,传入 getCredentials 函数避免死锁
575
+ // 不调用 setCredentials,由 OAuth2Client._initialize 负责保存
576
+ try {
577
+ user = await this.getUserInfo({ credentials })
578
+ } catch (e) {
579
+ console.error('get user info error', e)
580
+ // 获取 user 失败不影响登录流程
581
+ }
582
+
583
+ res = { data: { ...data, session: credentials, user }, error: null }
584
+ }
585
+
586
+ // Clean up URL parameters and restore original URL state
587
+ localSearch.delete('code')
588
+ localSearch.delete('state')
589
+ localSearch.delete('provider')
590
+ this.restoreUrlState(
591
+ cacheData?.search === undefined ? `?${localSearch.toString()}` : cacheData.search,
592
+ cacheData?.hash || location.hash,
593
+ )
594
+
595
+ // Remove session storage
596
+ try {
597
+ sessionStorage.removeItem(state)
598
+ } catch {
599
+ // ignore
600
+ }
601
+
602
+ return res
603
+ } catch (error) {
604
+ return { data, error: new AuthError(error) }
605
+ }
606
+ }
607
+
446
608
  /**
447
609
  * Signin with custom.
448
610
  * @param {SignInCustomRequest} params A SignInCustomRequest object.
@@ -476,11 +638,12 @@ export class Auth {
476
638
  * @param {BindWithProviderRequest} params A BindWithProviderRequest object.
477
639
  * @return {Promise<void>} A Promise<any> object.
478
640
  */
479
- public async bindWithProvider(params: BindWithProviderRequest): Promise<void> {
641
+ public async bindWithProvider(params: BindWithProviderRequest, credentials?: Credentials): Promise<void> {
480
642
  return this.config.request<any>(ApiUrls.PROVIDER_BIND_URL, {
481
643
  method: 'POST',
482
644
  body: params,
483
645
  withCredentials: true,
646
+ getCredentials: credentials ? () => credentials : undefined,
484
647
  })
485
648
  }
486
649
 
@@ -494,9 +657,14 @@ export class Auth {
494
657
 
495
658
  /**
496
659
  * Get the user info.
660
+ * @param params.getCredentials Optional custom getCredentials function to bypass default getCredentials() and avoid deadlock
497
661
  * @return {Promise<UserInfo>} A Promise<UserProfile> object.
498
662
  */
499
- public async getUserInfo(params: { version?: string; query?: string } = {}): Promise<UserInfo> {
663
+ public async getUserInfo(params: {
664
+ version?: string
665
+ query?: string
666
+ credentials?: Credentials
667
+ } = {},): Promise<UserInfo> {
500
668
  const res = this.getParamsByVersion(params, 'USER_ME_URL')
501
669
 
502
670
  if (res.params?.query) {
@@ -507,6 +675,7 @@ export class Auth {
507
675
  const userInfo = await this.config.request<UserInfo>(res.url, {
508
676
  method: 'GET',
509
677
  withCredentials: true,
678
+ getCredentials: params.credentials ? () => params.credentials : undefined,
510
679
  })
511
680
 
512
681
  if (userInfo.sub) {
@@ -708,7 +877,8 @@ export class Auth {
708
877
  }
709
878
 
710
879
  /**
711
- * Patch the user profile.
880
+ * Patch the user profile. 没有和数据源同步
881
+ * @deprecated use updateUserBasicInfo
712
882
  * @param {UserProfile} params A UserProfile Object.
713
883
  * @return {Promise<UserProfile>} A Promise<UserProfile> object.
714
884
  */
@@ -1102,4 +1272,19 @@ export class Auth {
1102
1272
  },
1103
1273
  })
1104
1274
  }
1275
+
1276
+ /**
1277
+ * Restore URL state after OAuth callback
1278
+ */
1279
+ private restoreUrlState(search?: string, hash?: string): void {
1280
+ if (search === undefined) return
1281
+ try {
1282
+ const url = new URL(window.location.href)
1283
+ url.search = search
1284
+ url.hash = hash || ''
1285
+ window.history.replaceState(null, '', url.toString())
1286
+ } catch {
1287
+ // ignore
1288
+ }
1289
+ }
1105
1290
  }
@@ -0,0 +1,21 @@
1
+ export class AuthError extends Error {
2
+ /**
3
+ * Error code associated with the error. Most errors coming from
4
+ * HTTP responses will have a code, though some errors that occur
5
+ * before a response is received will not have one present. In that
6
+ * case {@link #status} will also be undefined.
7
+ */
8
+ code: (string & {}) | undefined
9
+
10
+ /** HTTP status code that caused the error. */
11
+ status: number | undefined
12
+
13
+ protected __isAuthError = true
14
+
15
+ constructor(error) {
16
+ super(error.error_description || error.message)
17
+ this.name = 'AuthError'
18
+ this.status = error.error
19
+ this.code = error.error_code
20
+ }
21
+ }
@@ -119,3 +119,31 @@ export enum ErrorType {
119
119
  CAPTCHA_REQUIRED = 'captcha_required',
120
120
  CAPTCHA_INVALID = 'captcha_invalid',
121
121
  }
122
+
123
+ export const LOGIN_STATE_CHANGED_TYPE = {
124
+ SIGN_OUT: 'sign_out',
125
+ SIGN_IN: 'sign_in',
126
+ CREDENTIALS_ERROR: 'credentials_error',
127
+ }
128
+
129
+ export const AUTH_STATE_CHANGED_TYPE = {
130
+ SIGNED_OUT: 'SIGNED_OUT',
131
+ SIGNED_IN: 'SIGNED_IN',
132
+ INITIAL_SESSION: 'INITIAL_SESSION',
133
+ PASSWORD_RECOVERY: 'PASSWORD_RECOVERY',
134
+ TOKEN_REFRESHED: 'TOKEN_REFRESHED',
135
+ USER_UPDATED: 'USER_UPDATED',
136
+ BIND_IDENTITY: 'BIND_IDENTITY',
137
+ }
138
+
139
+ export const EVENTS = {
140
+ // 登录态改变后触发
141
+ LOGIN_STATE_CHANGED: 'loginStateChanged',
142
+ // 授权态改变后触发
143
+ AUTH_STATE_CHANGED: 'authStateChanged',
144
+ }
145
+
146
+ export const OAUTH_TYPE = {
147
+ SIGN_IN: 'sign_in',
148
+ BIND_IDENTITY: 'bind_identity',
149
+ }
@@ -622,6 +622,8 @@ export interface ModifyUserBasicInfoRequest {
622
622
  gender?: 'MALE' | 'FEMALE'
623
623
  password?: string // 旧密码
624
624
  new_password?: string // 新密码
625
+ email?: string
626
+ phone?: string
625
627
  }
626
628
 
627
629
  export interface GetUserBehaviorLog {