@auth0/auth0-spa-js 2.4.1 → 2.6.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 (42) hide show
  1. package/README.md +1 -1
  2. package/dist/auth0-spa-js.development.js +298 -43
  3. package/dist/auth0-spa-js.development.js.map +1 -1
  4. package/dist/auth0-spa-js.production.esm.js +1 -1
  5. package/dist/auth0-spa-js.production.esm.js.map +1 -1
  6. package/dist/auth0-spa-js.production.js +1 -1
  7. package/dist/auth0-spa-js.production.js.map +1 -1
  8. package/dist/auth0-spa-js.worker.development.js +34 -2
  9. package/dist/auth0-spa-js.worker.development.js.map +1 -1
  10. package/dist/auth0-spa-js.worker.production.js +1 -1
  11. package/dist/auth0-spa-js.worker.production.js.map +1 -1
  12. package/dist/lib/auth0-spa-js.cjs.js +309 -44
  13. package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
  14. package/dist/typings/Auth0Client.d.ts +42 -2
  15. package/dist/typings/Auth0Client.utils.d.ts +32 -0
  16. package/dist/typings/MyAccountApiClient.d.ts +92 -0
  17. package/dist/typings/api.d.ts +1 -1
  18. package/dist/typings/cache/cache-manager.d.ts +18 -1
  19. package/dist/typings/errors.d.ts +10 -0
  20. package/dist/typings/fetcher.d.ts +11 -7
  21. package/dist/typings/global.d.ts +97 -0
  22. package/dist/typings/http.d.ts +2 -2
  23. package/dist/typings/index.d.ts +2 -1
  24. package/dist/typings/transaction-manager.d.ts +15 -4
  25. package/dist/typings/version.d.ts +1 -1
  26. package/dist/typings/worker/worker.types.d.ts +1 -0
  27. package/package.json +1 -1
  28. package/src/Auth0Client.ts +282 -25
  29. package/src/Auth0Client.utils.ts +66 -0
  30. package/src/MyAccountApiClient.ts +158 -0
  31. package/src/api.ts +7 -1
  32. package/src/cache/cache-manager.ts +82 -7
  33. package/src/errors.ts +18 -0
  34. package/src/fetcher.ts +30 -18
  35. package/src/global.ts +112 -4
  36. package/src/http.ts +12 -5
  37. package/src/index.ts +5 -0
  38. package/src/transaction-manager.ts +17 -4
  39. package/src/utils.ts +1 -0
  40. package/src/version.ts +1 -1
  41. package/src/worker/token.worker.ts +60 -9
  42. package/src/worker/worker.types.ts +1 -0
@@ -0,0 +1,158 @@
1
+ import { AuthorizationParams } from './global';
2
+ import { Fetcher } from './fetcher';
3
+
4
+ interface ConnectRequest {
5
+ /** The name of the connection to link the account with (e.g., 'google-oauth2', 'facebook'). */
6
+ connection: string;
7
+ /** The URI to redirect to after the connection process completes. */
8
+ redirect_uri: string;
9
+ /** An opaque value used to maintain state between the request and callback. */
10
+ state?: string;
11
+ /** A string value used to associate a Client session with an ID Token, and to mitigate replay attacks. */
12
+ nonce?: string;
13
+ /** The PKCE code challenge derived from the code verifier. */
14
+ code_challenge?: string;
15
+ /** The method used to derive the code challenge. Required when code_challenge is provided. */
16
+ code_challenge_method?: 'S256';
17
+ authorization_params?: AuthorizationParams;
18
+ }
19
+
20
+ interface ConnectResponse {
21
+ /** The base URI to initiate the account connection flow. */
22
+ connect_uri: string;
23
+ /** The authentication session identifier. */
24
+ auth_session: string;
25
+ /** Parameters to be used with the connect URI. */
26
+ connect_params: {
27
+ /** The ticket identifier to be used with the connection URI. */
28
+ ticket: string;
29
+ };
30
+ /** The number of seconds until the ticket expires. */
31
+ expires_in: number;
32
+ }
33
+
34
+ interface CompleteRequest {
35
+ /** The authentication session identifier */
36
+ auth_session: string;
37
+ /** The authorization code returned from the connect flow */
38
+ connect_code: string;
39
+ /** The redirect URI used in the original request */
40
+ redirect_uri: string;
41
+ /** The PKCE code verifier */
42
+ code_verifier?: string;
43
+ }
44
+
45
+ export interface CompleteResponse {
46
+ /** The unique identifier of the connected account */
47
+ id: string;
48
+ /** The connection name */
49
+ connection: string;
50
+ /** The access type, always 'offline' */
51
+ access_type: 'offline';
52
+ /** Array of scopes granted */
53
+ scopes?: string[];
54
+ /** ISO date string of when the connected account was created */
55
+ created_at: string;
56
+ /** ISO date string of when the refresh token expires (optional) */
57
+ expires_at?: string;
58
+ }
59
+
60
+ // Validation error returned from MyAccount API
61
+ export interface ErrorResponse {
62
+ type: string;
63
+ status: number;
64
+ title: string;
65
+ detail: string;
66
+ validation_errors?: {
67
+ detail: string;
68
+ field?: string;
69
+ pointer?: string;
70
+ source?: string;
71
+ }[];
72
+ }
73
+
74
+ /**
75
+ * Subset of the MyAccount API that handles the connect accounts flow.
76
+ */
77
+ export class MyAccountApiClient {
78
+ constructor(
79
+ private myAccountFetcher: Fetcher<Response>,
80
+ private apiBase: string
81
+ ) {}
82
+
83
+ /**
84
+ * Get a ticket for the connect account flow.
85
+ */
86
+ async connectAccount(params: ConnectRequest): Promise<ConnectResponse> {
87
+ const res = await this.myAccountFetcher.fetchWithAuth(
88
+ `${this.apiBase}v1/connected-accounts/connect`,
89
+ {
90
+ method: 'POST',
91
+ headers: { 'Content-Type': 'application/json' },
92
+ body: JSON.stringify(params)
93
+ }
94
+ );
95
+ return this._handleResponse(res);
96
+ }
97
+
98
+ /**
99
+ * Verify the redirect from the connect account flow and complete the connecting of the account.
100
+ */
101
+ async completeAccount(params: CompleteRequest): Promise<CompleteResponse> {
102
+ const res = await this.myAccountFetcher.fetchWithAuth(
103
+ `${this.apiBase}v1/connected-accounts/complete`,
104
+ {
105
+ method: 'POST',
106
+ headers: { 'Content-Type': 'application/json' },
107
+ body: JSON.stringify(params)
108
+ }
109
+ );
110
+ return this._handleResponse(res);
111
+ }
112
+
113
+ private async _handleResponse(res: Response) {
114
+ let body: any;
115
+ try {
116
+ body = await res.text();
117
+ body = JSON.parse(body);
118
+ } catch (err) {
119
+ throw new MyAccountApiError({
120
+ type: 'invalid_json',
121
+ status: res.status,
122
+ title: 'Invalid JSON response',
123
+ detail: body || String(err)
124
+ });
125
+ }
126
+
127
+ if (res.ok) {
128
+ return body;
129
+ } else {
130
+ throw new MyAccountApiError(body);
131
+ }
132
+ }
133
+ }
134
+
135
+ export class MyAccountApiError extends Error {
136
+ public readonly type: string;
137
+ public readonly status: number;
138
+ public readonly title: string;
139
+ public readonly detail: string;
140
+ public readonly validation_errors?: ErrorResponse['validation_errors'];
141
+
142
+ constructor({
143
+ type,
144
+ status,
145
+ title,
146
+ detail,
147
+ validation_errors
148
+ }: ErrorResponse) {
149
+ super(detail);
150
+ this.name = 'MyAccountApiError';
151
+ this.type = type;
152
+ this.status = status;
153
+ this.title = title;
154
+ this.detail = detail;
155
+ this.validation_errors = validation_errors;
156
+ Object.setPrototypeOf(this, MyAccountApiError.prototype);
157
+ }
158
+ }
package/src/api.ts CHANGED
@@ -12,6 +12,7 @@ export async function oauthToken(
12
12
  scope,
13
13
  auth0Client,
14
14
  useFormData,
15
+ useMrrt,
15
16
  dpop,
16
17
  ...options
17
18
  }: TokenEndpointOptions,
@@ -20,10 +21,14 @@ export async function oauthToken(
20
21
  const isTokenExchange =
21
22
  options.grant_type === 'urn:ietf:params:oauth:grant-type:token-exchange';
22
23
 
24
+ const refreshWithMrrt =
25
+ options.grant_type === 'refresh_token' && useMrrt;
26
+
23
27
  const allParams = {
24
28
  ...options,
25
29
  ...(isTokenExchange && audience && { audience }),
26
- ...(isTokenExchange && scope && { scope })
30
+ ...(isTokenExchange && scope && { scope }),
31
+ ...(refreshWithMrrt && { audience, scope })
27
32
  };
28
33
 
29
34
  const body = useFormData
@@ -51,6 +56,7 @@ export async function oauthToken(
51
56
  },
52
57
  worker,
53
58
  useFormData,
59
+ useMrrt,
54
60
  isDpopSupported ? dpop : undefined
55
61
  );
56
62
  }
@@ -69,7 +69,9 @@ export class CacheManager {
69
69
 
70
70
  async get(
71
71
  cacheKey: CacheKey,
72
- expiryAdjustmentSeconds = DEFAULT_EXPIRY_ADJUSTMENT_SECONDS
72
+ expiryAdjustmentSeconds = DEFAULT_EXPIRY_ADJUSTMENT_SECONDS,
73
+ useMrrt = false,
74
+ cacheMode?: string
73
75
  ): Promise<Partial<CacheEntry> | undefined> {
74
76
  let wrappedEntry = await this.cache.get<WrappedCacheEntry>(
75
77
  cacheKey.toKey()
@@ -85,6 +87,13 @@ export class CacheManager {
85
87
  if (matchedKey) {
86
88
  wrappedEntry = await this.cache.get<WrappedCacheEntry>(matchedKey);
87
89
  }
90
+
91
+ // To refresh using MRRT we need to send a request to the server
92
+ // If cacheMode is 'cache-only', this will make us unable to call the server
93
+ // so it won't be needed to find a valid refresh token
94
+ if (!matchedKey && useMrrt && cacheMode !== 'cache-only') {
95
+ return this.getEntryWithRefreshToken(cacheKey, keys);
96
+ }
88
97
  }
89
98
 
90
99
  // If we still don't have an entry, exit.
@@ -97,12 +106,7 @@ export class CacheManager {
97
106
 
98
107
  if (wrappedEntry.expiresAt - expiryAdjustmentSeconds < nowSeconds) {
99
108
  if (wrappedEntry.body.refresh_token) {
100
- wrappedEntry.body = {
101
- refresh_token: wrappedEntry.body.refresh_token
102
- };
103
-
104
- await this.cache.set(cacheKey.toKey(), wrappedEntry);
105
- return wrappedEntry.body;
109
+ return this.modifiedCachedEntry(wrappedEntry, cacheKey);
106
110
  }
107
111
 
108
112
  await this.cache.remove(cacheKey.toKey());
@@ -114,6 +118,24 @@ export class CacheManager {
114
118
  return wrappedEntry.body;
115
119
  }
116
120
 
121
+ private async modifiedCachedEntry(wrappedEntry: WrappedCacheEntry, cacheKey: CacheKey): Promise<Partial<CacheEntry>> {
122
+ // We need to keep audience and scope in order to check them later when doing refresh
123
+ // using MRRT. See getScopeToRequest method.
124
+ wrappedEntry.body = {
125
+ refresh_token: wrappedEntry.body.refresh_token,
126
+ audience: wrappedEntry.body.audience,
127
+ scope: wrappedEntry.body.scope,
128
+ };
129
+
130
+ await this.cache.set(cacheKey.toKey(), wrappedEntry);
131
+
132
+ return {
133
+ refresh_token: wrappedEntry.body.refresh_token,
134
+ audience: wrappedEntry.body.audience,
135
+ scope: wrappedEntry.body.scope,
136
+ };
137
+ }
138
+
117
139
  async set(entry: CacheEntry): Promise<void> {
118
140
  const cacheKey = new CacheKey({
119
141
  clientId: entry.client_id,
@@ -207,4 +229,57 @@ export class CacheManager {
207
229
  );
208
230
  })[0];
209
231
  }
232
+
233
+ /**
234
+ * Returns the first entry that contains a refresh_token that satisfies the following conditions
235
+ * The keys inside the cache are in the format {prefix}::{clientId}::{audience}::{scope}.
236
+ * - `prefix` is strict equal to Auth0's internally configured `keyPrefix`
237
+ * - `clientId` is strict equal to the `cacheKey.clientId`
238
+ * @param keyToMatch The provided cache key
239
+ * @param allKeys A list of existing cache keys
240
+ */
241
+ private async getEntryWithRefreshToken(keyToMatch: CacheKey, allKeys: Array<string>): Promise<Partial<CacheEntry> | undefined> {
242
+ for (const key of allKeys) {
243
+ const cacheKey = CacheKey.fromKey(key);
244
+
245
+ if (cacheKey.prefix === CACHE_KEY_PREFIX &&
246
+ cacheKey.clientId === keyToMatch.clientId) {
247
+ const cachedEntry = await this.cache.get<WrappedCacheEntry>(key);
248
+
249
+ if (cachedEntry?.body?.refresh_token) {
250
+ return this.modifiedCachedEntry(cachedEntry, keyToMatch);
251
+ }
252
+ }
253
+ }
254
+
255
+ return undefined;
256
+ }
257
+
258
+ /**
259
+ * Updates in the cache all entries that has a match with previous refresh_token with the
260
+ * new refresh_token obtained from the server
261
+ * @param oldRefreshToken Old refresh_token used on refresh
262
+ * @param newRefreshToken New refresh_token obtained from the server after refresh
263
+ */
264
+ async updateEntry(
265
+ oldRefreshToken: string,
266
+ newRefreshToken: string,
267
+ ): Promise<void> {
268
+ const allKeys = await this.getCacheKeys();
269
+
270
+ if (!allKeys) return;
271
+
272
+ for (const key of allKeys) {
273
+ const entry = await this.cache.get<WrappedCacheEntry>(key);
274
+
275
+ if (entry?.body?.refresh_token === oldRefreshToken) {
276
+ const cacheEntry = {
277
+ ...entry.body,
278
+ refresh_token: newRefreshToken,
279
+ } as CacheEntry;
280
+
281
+ await this.set(cacheEntry);
282
+ }
283
+ }
284
+ }
210
285
  }
package/src/errors.ts CHANGED
@@ -35,6 +35,24 @@ export class AuthenticationError extends GenericError {
35
35
  }
36
36
  }
37
37
 
38
+ /**
39
+ * Thrown when handling the redirect callback for the connect flow fails, will be one of Auth0's
40
+ * Authentication API's Standard Error Responses: https://auth0.com/docs/api/authentication?javascript#standard-error-responses
41
+ */
42
+ export class ConnectError extends GenericError {
43
+ constructor(
44
+ error: string,
45
+ error_description: string,
46
+ public connection: string,
47
+ public state: string,
48
+ public appState: any = null
49
+ ) {
50
+ super(error, error_description);
51
+ //https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
52
+ Object.setPrototypeOf(this, ConnectError.prototype);
53
+ }
54
+ }
55
+
38
56
  /**
39
57
  * Thrown when silent auth times out (usually due to a configuration issue) or
40
58
  * when network requests to the Auth server timeout.
package/src/fetcher.ts CHANGED
@@ -15,7 +15,12 @@ export type CustomFetchImpl<TOutput extends CustomFetchMinimalOutput> = (
15
15
  req: Request
16
16
  ) => Promise<TOutput>;
17
17
 
18
- type AccessTokenFactory = () => Promise<string>;
18
+ export type AuthParams = {
19
+ scope?: string[];
20
+ audience?: string;
21
+ };
22
+
23
+ type AccessTokenFactory = (authParams?: AuthParams) => Promise<string>;
19
24
 
20
25
  export type FetcherConfig<TOutput extends CustomFetchMinimalOutput> = {
21
26
  getAccessToken?: AccessTokenFactory;
@@ -26,7 +31,7 @@ export type FetcherConfig<TOutput extends CustomFetchMinimalOutput> = {
26
31
 
27
32
  export type FetcherHooks = {
28
33
  isDpopEnabled: () => boolean;
29
- getAccessToken: () => Promise<string>;
34
+ getAccessToken: AccessTokenFactory;
30
35
  getDpopNonce: () => Promise<string | undefined>;
31
36
  setDpopNonce: (nonce: string) => Promise<void>;
32
37
  generateDpopProof: (params: {
@@ -83,10 +88,10 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
83
88
  throw new TypeError('`url` must be absolute or `baseUrl` non-empty.');
84
89
  }
85
90
 
86
- protected getAccessToken(): Promise<string> {
91
+ protected getAccessToken(authParams?: AuthParams): Promise<string> {
87
92
  return this.config.getAccessToken
88
- ? this.config.getAccessToken()
89
- : this.hooks.getAccessToken();
93
+ ? this.config.getAccessToken(authParams)
94
+ : this.hooks.getAccessToken(authParams);
90
95
  }
91
96
 
92
97
  protected buildBaseRequest(
@@ -109,10 +114,10 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
109
114
  );
110
115
  }
111
116
 
112
- protected async setAuthorizationHeader(
117
+ protected setAuthorizationHeader(
113
118
  request: Request,
114
119
  accessToken: string
115
- ): Promise<void> {
120
+ ): void {
116
121
  request.headers.set(
117
122
  'authorization',
118
123
  `${this.config.dpopNonceId ? 'DPoP' : 'Bearer'} ${accessToken}`
@@ -139,8 +144,8 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
139
144
  request.headers.set('dpop', dpopProof);
140
145
  }
141
146
 
142
- protected async prepareRequest(request: Request) {
143
- const accessToken = await this.getAccessToken();
147
+ protected async prepareRequest(request: Request, authParams?: AuthParams) {
148
+ const accessToken = await this.getAccessToken(authParams);
144
149
 
145
150
  this.setAuthorizationHeader(request, accessToken);
146
151
 
@@ -195,11 +200,12 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
195
200
  protected async internalFetchWithAuth(
196
201
  info: RequestInfo | URL,
197
202
  init: RequestInit | undefined,
198
- callbacks: FetchWithAuthCallbacks<TOutput>
203
+ callbacks: FetchWithAuthCallbacks<TOutput>,
204
+ authParams?: AuthParams
199
205
  ): Promise<TOutput> {
200
206
  const request = this.buildBaseRequest(info, init);
201
207
 
202
- await this.prepareRequest(request);
208
+ await this.prepareRequest(request, authParams);
203
209
 
204
210
  const response = await this.config.fetch(request);
205
211
 
@@ -208,17 +214,23 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
208
214
 
209
215
  public fetchWithAuth(
210
216
  info: RequestInfo | URL,
211
- init?: RequestInit
217
+ init?: RequestInit,
218
+ authParams?: AuthParams
212
219
  ): Promise<TOutput> {
213
220
  const callbacks: FetchWithAuthCallbacks<TOutput> = {
214
221
  onUseDpopNonceError: () =>
215
- this.internalFetchWithAuth(info, init, {
216
- ...callbacks,
217
- // Retry on a `use_dpop_nonce` error, but just once.
218
- onUseDpopNonceError: undefined
219
- })
222
+ this.internalFetchWithAuth(
223
+ info,
224
+ init,
225
+ {
226
+ ...callbacks,
227
+ // Retry on a `use_dpop_nonce` error, but just once.
228
+ onUseDpopNonceError: undefined
229
+ },
230
+ authParams
231
+ )
220
232
  };
221
233
 
222
- return this.internalFetchWithAuth(info, init, callbacks);
234
+ return this.internalFetchWithAuth(info, init, callbacks, authParams);
223
235
  }
224
236
  }
package/src/global.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ICache } from './cache';
2
2
  import type { Dpop } from './dpop/dpop';
3
+ import { CompleteResponse } from './MyAccountApiClient';
3
4
 
4
5
  export interface AuthorizationParams {
5
6
  /**
@@ -265,14 +266,20 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
265
266
 
266
267
  /**
267
268
  * If provided, the SDK will load the token worker from this URL instead of the integrated `blob`. An example of when this is useful is if you have strict
268
- * Content-Security-Policy (CSP) and wish to avoid needing to set `worker-src: blob:`. We recommend either serving the worker, which you can find in the module
269
- * at `<module_path>/dist/auth0-spa-js.worker.production.js`, from the same host as your application or using the Auth0 CDN
269
+ * Content-Security-Policy (CSP) and wish to avoid needing to set `worker-src: blob:`. We recommend either serving the worker, which you can find in the module
270
+ * at `<module_path>/dist/auth0-spa-js.worker.production.js`, from the same host as your application or using the Auth0 CDN
270
271
  * `https://cdn.auth0.com/js/auth0-spa-js/<version>/auth0-spa-js.worker.production.js`.
271
- *
272
+ *
272
273
  * **Note**: The worker is only used when `useRefreshTokens: true`, `cacheLocation: 'memory'`, and the `cache` is not custom.
273
274
  */
274
275
  workerUrl?: string;
275
276
 
277
+
278
+ /**
279
+ * If `true`, the SDK will allow the refreshing of tokens using MRRT
280
+ */
281
+ useMrrt?: boolean;
282
+
276
283
  /**
277
284
  * If `true`, DPoP (OAuth 2.0 Demonstrating Proof of Possession, RFC9449)
278
285
  * will be used to cryptographically bind tokens to this specific browser
@@ -347,14 +354,29 @@ export interface RedirectLoginOptions<TAppState = any>
347
354
  openUrl?: (url: string) => Promise<void> | void;
348
355
  }
349
356
 
357
+ /**
358
+ * The types of responses expected from the authorization server.
359
+ * - `code`: used for the standard login flow.
360
+ * - `connect_code`: used for the connect account flow.
361
+ */
362
+ export enum ResponseType {
363
+ Code = 'code',
364
+ ConnectCode = 'connect_code'
365
+ }
366
+
350
367
  export interface RedirectLoginResult<TAppState = any> {
351
368
  /**
352
369
  * State stored when the redirect request was made
353
370
  */
354
371
  appState?: TAppState;
372
+
373
+ /**
374
+ * The type of response, for login it will be `code`
375
+ */
376
+ response_type: ResponseType.Code;
355
377
  }
356
378
 
357
- export interface PopupLoginOptions extends BaseLoginOptions {}
379
+ export interface PopupLoginOptions extends BaseLoginOptions { }
358
380
 
359
381
  export interface PopupConfigOptions {
360
382
  /**
@@ -517,12 +539,98 @@ export interface LogoutOptions extends LogoutUrlOptions {
517
539
  openUrl?: false | ((url: string) => Promise<void> | void);
518
540
  }
519
541
 
542
+ export interface RedirectConnectAccountOptions<TAppState = any> {
543
+ /**
544
+ * The name of the connection to link (e.g. 'google-oauth2').
545
+ */
546
+ connection: string;
547
+
548
+ /**
549
+ * Additional authorization parameters for the request.
550
+ *
551
+ * @example
552
+ * await auth0.connectAccountWithRedirect({
553
+ * connection: 'google-oauth2',
554
+ * authorization_params: {
555
+ * scope: 'https://www.googleapis.com/auth/calendar'
556
+ * access_type: 'offline'
557
+ * }
558
+ * });
559
+ *
560
+ * @example
561
+ * await auth0.connectAccountWithRedirect({
562
+ * connection: 'github',
563
+ * authorization_params: {
564
+ * scope: 'repo user',
565
+ * audience: 'https://api.github.com'
566
+ * }
567
+ * });
568
+ */
569
+ authorization_params?: AuthorizationParams;
570
+
571
+ /**
572
+ * The URI to redirect back to after connecting the account.
573
+ */
574
+ redirectUri?: string;
575
+
576
+ /**
577
+ * Optional application state to persist through the transaction.
578
+ *
579
+ * @example
580
+ * await auth0.connectAccountWithRedirect({
581
+ * connection: 'google-oauth2',
582
+ * appState: { returnTo: '/settings' }
583
+ * });
584
+ */
585
+ appState?: TAppState;
586
+
587
+ /**
588
+ * Optional function to handle the redirect URL.
589
+ *
590
+ * @example
591
+ * await auth0.connectAccountWithRedirect({
592
+ * connection: 'google-oauth2',
593
+ * openUrl: async (url) => { myBrowserApi.open(url); }
594
+ * });
595
+ */
596
+ openUrl?: (url: string) => Promise<void>;
597
+ }
598
+
599
+ /**
600
+ * The result returned after a successful account connection redirect.
601
+ *
602
+ * Combines the redirect login result (including any persisted app state)
603
+ * with the complete response from the My Account API.
604
+ *
605
+ * @template TAppState - The type of application state persisted through the transaction.
606
+ * @example
607
+ * const result = await auth0.connectAccountWithRedirect(options);
608
+ * console.log(result.appState); // Access persisted app state
609
+ * console.log(result.connection); // The connection of the account you connected to.
610
+ * console.log(result.response_type === 'connect_code'); // The response type will be 'connect_code'
611
+ */
612
+ export type ConnectAccountRedirectResult<TAppState = any> = CompleteResponse & {
613
+ /**
614
+ * State stored when the redirect request was made
615
+ */
616
+ appState?: TAppState;
617
+
618
+ /**
619
+ * The type of response, for connect account it will be `connect_code`
620
+ */
621
+ response_type: ResponseType.ConnectCode;
622
+ };
623
+
520
624
  /**
521
625
  * @ignore
522
626
  */
523
627
  export interface AuthenticationResult {
524
628
  state: string;
525
629
  code?: string;
630
+ /**
631
+ * This is for the redirect from the connect account flow.
632
+ */
633
+ connect_code?: string;
526
634
  error?: string;
527
635
  error_description?: string;
528
636
  }
package/src/http.ts CHANGED
@@ -65,7 +65,8 @@ const fetchWithWorker = async (
65
65
  fetchOptions: FetchOptions,
66
66
  timeout: number,
67
67
  worker: Worker,
68
- useFormData?: boolean
68
+ useFormData?: boolean,
69
+ useMrrt?: boolean
69
70
  ) => {
70
71
  return sendMessage(
71
72
  {
@@ -76,7 +77,8 @@ const fetchWithWorker = async (
76
77
  timeout,
77
78
  fetchUrl,
78
79
  fetchOptions,
79
- useFormData
80
+ useFormData,
81
+ useMrrt
80
82
  },
81
83
  worker
82
84
  );
@@ -89,7 +91,8 @@ export const switchFetch = async (
89
91
  fetchOptions: FetchOptions,
90
92
  worker?: Worker,
91
93
  useFormData?: boolean,
92
- timeout = DEFAULT_FETCH_TIMEOUT_MS
94
+ timeout = DEFAULT_FETCH_TIMEOUT_MS,
95
+ useMrrt?: boolean,
93
96
  ): Promise<any> => {
94
97
  if (worker) {
95
98
  return fetchWithWorker(
@@ -99,7 +102,8 @@ export const switchFetch = async (
99
102
  fetchOptions,
100
103
  timeout,
101
104
  worker,
102
- useFormData
105
+ useFormData,
106
+ useMrrt
103
107
  );
104
108
  } else {
105
109
  return fetchWithoutWorker(fetchUrl, fetchOptions, timeout);
@@ -114,6 +118,7 @@ export async function getJSON<T>(
114
118
  options: FetchOptions,
115
119
  worker?: Worker,
116
120
  useFormData?: boolean,
121
+ useMrrt?: boolean,
117
122
  dpop?: Pick<Dpop, 'generateProof' | 'getNonce' | 'setNonce'>,
118
123
  isDpopRetry?: boolean
119
124
  ): Promise<T> {
@@ -139,7 +144,8 @@ export async function getJSON<T>(
139
144
  options,
140
145
  worker,
141
146
  useFormData,
142
- timeout
147
+ timeout,
148
+ useMrrt,
143
149
  );
144
150
  fetchError = null;
145
151
  break;
@@ -210,6 +216,7 @@ export async function getJSON<T>(
210
216
  options,
211
217
  worker,
212
218
  useFormData,
219
+ useMrrt,
213
220
  dpop,
214
221
  true // !
215
222
  );
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export async function createAuth0Client(options: Auth0ClientOptions) {
23
23
  export { Auth0Client };
24
24
 
25
25
  export {
26
+ ConnectError,
26
27
  GenericError,
27
28
  AuthenticationError,
28
29
  TimeoutError,
@@ -48,3 +49,7 @@ export {
48
49
  } from './cache';
49
50
 
50
51
  export { type FetcherConfig } from './fetcher';
52
+
53
+ export {
54
+ MyAccountApiError
55
+ } from './MyAccountApiClient';