@auth0/auth0-spa-js 2.18.3 → 2.19.1

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 (61) hide show
  1. package/README.md +1 -1
  2. package/dist/auth0-spa-js.development.js +427 -370
  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 +132 -81
  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 +449 -393
  13. package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
  14. package/dist/typings/Auth0Client.d.ts +476 -439
  15. package/dist/typings/Auth0Client.utils.d.ts +90 -90
  16. package/dist/typings/MyAccountApiClient.d.ts +92 -92
  17. package/dist/typings/TokenExchange.d.ts +77 -77
  18. package/dist/typings/api.d.ts +33 -2
  19. package/dist/typings/cache/cache-localstorage.d.ts +7 -7
  20. package/dist/typings/cache/cache-manager.d.ts +69 -56
  21. package/dist/typings/cache/cache-memory.d.ts +4 -4
  22. package/dist/typings/cache/index.d.ts +4 -4
  23. package/dist/typings/cache/key-manifest.d.ts +12 -12
  24. package/dist/typings/cache/shared.d.ts +68 -68
  25. package/dist/typings/constants.d.ts +58 -58
  26. package/dist/typings/dpop/dpop.d.ts +17 -17
  27. package/dist/typings/dpop/storage.d.ts +27 -27
  28. package/dist/typings/dpop/utils.d.ts +15 -15
  29. package/dist/typings/errors.d.ts +96 -96
  30. package/dist/typings/fetcher.d.ts +54 -54
  31. package/dist/typings/global.d.ts +826 -819
  32. package/dist/typings/http.d.ts +11 -5
  33. package/dist/typings/index.d.ts +24 -24
  34. package/dist/typings/jwt.d.ts +21 -21
  35. package/dist/typings/lock.d.ts +32 -32
  36. package/dist/typings/mfa/MfaApiClient.d.ts +225 -225
  37. package/dist/typings/mfa/MfaContextManager.d.ts +79 -79
  38. package/dist/typings/mfa/constants.d.ts +23 -23
  39. package/dist/typings/mfa/errors.d.ts +117 -117
  40. package/dist/typings/mfa/index.d.ts +4 -4
  41. package/dist/typings/mfa/types.d.ts +181 -181
  42. package/dist/typings/mfa/utils.d.ts +23 -23
  43. package/dist/typings/promise-utils.d.ts +2 -2
  44. package/dist/typings/scope.d.ts +35 -35
  45. package/dist/typings/storage.d.ts +26 -26
  46. package/dist/typings/transaction-manager.d.ts +33 -33
  47. package/dist/typings/utils.d.ts +36 -36
  48. package/dist/typings/version.d.ts +2 -2
  49. package/dist/typings/worker/token.worker.d.ts +1 -1
  50. package/dist/typings/worker/worker.types.d.ts +27 -20
  51. package/dist/typings/worker/worker.utils.d.ts +13 -7
  52. package/package.json +2 -2
  53. package/src/Auth0Client.ts +73 -2
  54. package/src/api.ts +116 -2
  55. package/src/cache/cache-manager.ts +85 -9
  56. package/src/global.ts +8 -0
  57. package/src/http.ts +28 -21
  58. package/src/version.ts +1 -1
  59. package/src/worker/token.worker.ts +120 -5
  60. package/src/worker/worker.types.ts +17 -6
  61. package/src/worker/worker.utils.ts +18 -7
@@ -1,7 +1,11 @@
1
1
  import { MissingRefreshTokenError } from '../errors';
2
2
  import { FetchResponse } from '../global';
3
3
  import { createQueryParams, fromEntries } from '../utils';
4
- import { WorkerMessage, WorkerRefreshTokenMessage } from './worker.types';
4
+ import {
5
+ WorkerMessage,
6
+ WorkerRefreshTokenMessage,
7
+ WorkerRevokeTokenMessage
8
+ } from './worker.types';
5
9
 
6
10
  let refreshTokens: Record<string, string> = {};
7
11
  let allowedBaseUrl: string | null = null;
@@ -22,6 +26,24 @@ const setRefreshToken = (
22
26
  const deleteRefreshToken = (audience: string, scope: string) =>
23
27
  delete refreshTokens[cacheKey(audience, scope)];
24
28
 
29
+ const getRefreshTokensByAudience = (audience: string): string[] => {
30
+ const seen = new Set<string>();
31
+ Object.entries(refreshTokens).forEach(([key, token]) => {
32
+ if (cacheKeyContainsAudience(audience, key)) {
33
+ seen.add(token);
34
+ }
35
+ });
36
+ return Array.from(seen);
37
+ };
38
+
39
+ const deleteRefreshTokensByValue = (refreshToken: string): void => {
40
+ Object.entries(refreshTokens).forEach(([key, token]) => {
41
+ if (token === refreshToken) {
42
+ delete refreshTokens[key];
43
+ }
44
+ });
45
+ };
46
+
25
47
  const wait = (time: number) =>
26
48
  new Promise<void>(resolve => setTimeout(resolve, time));
27
49
 
@@ -181,8 +203,84 @@ const messageHandler = async ({
181
203
  }
182
204
  };
183
205
 
206
+ const revokeMessageHandler = async ({
207
+ data: { timeout, auth, fetchUrl, fetchOptions, useFormData },
208
+ ports: [port]
209
+ }: MessageEvent<WorkerRevokeTokenMessage>) => {
210
+ const { audience } = auth || {};
211
+
212
+ try {
213
+ const tokensToRevoke = getRefreshTokensByAudience(audience);
214
+
215
+ if (tokensToRevoke.length === 0) {
216
+ port.postMessage({ ok: true });
217
+ return;
218
+ }
219
+
220
+ // Parse the base body once; rebuild per RT so each request is independent.
221
+ const baseBody = useFormData
222
+ ? formDataToObject(fetchOptions.body as string)
223
+ : JSON.parse(fetchOptions.body as string);
224
+
225
+ for (const refreshToken of tokensToRevoke) {
226
+ const body = useFormData
227
+ ? createQueryParams({ ...baseBody, token: refreshToken })
228
+ : JSON.stringify({ ...baseBody, token: refreshToken });
229
+
230
+ let abortController: AbortController | undefined;
231
+ let signal: AbortSignal | undefined;
232
+
233
+ if (typeof AbortController === 'function') {
234
+ abortController = new AbortController();
235
+ signal = abortController.signal;
236
+ }
237
+
238
+ let timeoutId: ReturnType<typeof setTimeout>;
239
+ let response: void | Response;
240
+
241
+ try {
242
+ response = await Promise.race([
243
+ new Promise<void>(resolve => { timeoutId = setTimeout(resolve, timeout); }),
244
+ fetch(fetchUrl, { ...fetchOptions, body, signal })
245
+ ]).finally(() => clearTimeout(timeoutId));
246
+ } catch (error) {
247
+ port.postMessage({ error: error.message });
248
+ return;
249
+ }
250
+
251
+ if (!response) {
252
+ if (abortController) abortController.abort();
253
+ port.postMessage({ error: "Timeout when executing 'fetch'" });
254
+ return;
255
+ }
256
+
257
+ if (!response.ok) {
258
+ let errorDescription: string | undefined;
259
+ try {
260
+ const { error_description } = JSON.parse(await response.text());
261
+ errorDescription = error_description;
262
+ } catch {
263
+ // body absent or not valid JSON
264
+ }
265
+
266
+ port.postMessage({ error: errorDescription || `HTTP error ${response.status}` });
267
+ return;
268
+ }
269
+
270
+ deleteRefreshTokensByValue(refreshToken);
271
+ }
272
+
273
+ port.postMessage({ ok: true });
274
+ } catch (error) {
275
+ port.postMessage({
276
+ error: error.message || 'Unknown error during token revocation'
277
+ });
278
+ }
279
+ };
280
+
184
281
  const isAuthorizedWorkerRequest = (
185
- workerRequest: WorkerRefreshTokenMessage
282
+ workerRequest: WorkerRefreshTokenMessage | WorkerRevokeTokenMessage,
283
+ expectedPath: string
186
284
  ) => {
187
285
  if (!allowedBaseUrl) {
188
286
  return false;
@@ -194,7 +292,7 @@ const isAuthorizedWorkerRequest = (
194
292
 
195
293
  return (
196
294
  requestedUrl.origin === allowedBaseOrigin &&
197
- requestedUrl.pathname === '/oauth/token'
295
+ requestedUrl.pathname === expectedPath
198
296
  );
199
297
  } catch {
200
298
  return false;
@@ -218,9 +316,26 @@ const messageRouter = (event: MessageEvent<WorkerMessage>) => {
218
316
  return;
219
317
  }
220
318
 
319
+ if ('type' in data && data.type === 'revoke') {
320
+ if (!isAuthorizedWorkerRequest(data as WorkerRevokeTokenMessage, '/oauth/revoke')) {
321
+ port?.postMessage({
322
+ ok: false,
323
+ json: {
324
+ error: 'invalid_fetch_url',
325
+ error_description: 'Unauthorized fetch URL'
326
+ },
327
+ headers: {}
328
+ });
329
+ return;
330
+ }
331
+
332
+ revokeMessageHandler(event as MessageEvent<WorkerRevokeTokenMessage>);
333
+ return;
334
+ }
335
+
221
336
  if (
222
337
  !('fetchUrl' in data) ||
223
- !isAuthorizedWorkerRequest(data as WorkerRefreshTokenMessage)
338
+ !isAuthorizedWorkerRequest(data as WorkerRefreshTokenMessage, '/oauth/token')
224
339
  ) {
225
340
  port?.postMessage({
226
341
  ok: false,
@@ -238,7 +353,7 @@ const messageRouter = (event: MessageEvent<WorkerMessage>) => {
238
353
 
239
354
  // Don't run `addEventListener` in our tests (this is replaced in rollup)
240
355
  if (process.env.NODE_ENV === 'test') {
241
- module.exports = { messageHandler, messageRouter };
356
+ module.exports = { messageHandler, revokeMessageHandler, messageRouter };
242
357
  /* c8 ignore next 4 */
243
358
  } else {
244
359
  // @ts-ignore
@@ -1,23 +1,34 @@
1
1
  import { FetchOptions } from '../global';
2
2
 
3
- /**
4
- * @ts-ignore
5
- */
6
3
  export type WorkerInitMessage = {
7
4
  type: 'init';
8
5
  allowedBaseUrl: string;
9
6
  };
10
7
 
11
- export type WorkerRefreshTokenMessage = {
8
+ type WorkerTokenMessage = {
12
9
  timeout: number;
13
10
  fetchUrl: string;
14
11
  fetchOptions: FetchOptions;
15
12
  useFormData?: boolean;
16
- useMrrt?: boolean;
17
13
  auth: {
18
14
  audience: string;
19
15
  scope: string;
20
16
  };
21
17
  };
22
18
 
23
- export type WorkerMessage = WorkerInitMessage | WorkerRefreshTokenMessage;
19
+ export type WorkerRefreshTokenMessage = WorkerTokenMessage & {
20
+ type: 'refresh';
21
+ useMrrt?: boolean;
22
+ };
23
+
24
+ export type WorkerRevokeTokenMessage = Omit<WorkerTokenMessage, 'auth'> & {
25
+ type: 'revoke';
26
+ auth: {
27
+ audience: string;
28
+ };
29
+ };
30
+
31
+ export type WorkerMessage =
32
+ | WorkerInitMessage
33
+ | WorkerRefreshTokenMessage
34
+ | WorkerRevokeTokenMessage;
@@ -1,16 +1,27 @@
1
- import { WorkerRefreshTokenMessage } from './worker.types';
1
+ import {
2
+ WorkerRefreshTokenMessage,
3
+ WorkerRevokeTokenMessage
4
+ } from './worker.types';
2
5
 
3
6
  /**
4
- * Sends the specified message to the web worker
5
- * @param message The message to send
6
- * @param to The worker to send the message to
7
+ * Sends a message to a Web Worker and returns a Promise that resolves with
8
+ * the worker's response, or rejects if the worker replies with an error.
9
+ *
10
+ * Uses a {@link MessageChannel} so each call gets its own private reply port,
11
+ * making concurrent calls safe without shared state.
12
+ *
13
+ * @param message - The typed message to send (`refresh` or `revoke`).
14
+ * @param to - The target {@link Worker} instance.
15
+ * @returns A Promise that resolves with the worker's response payload.
7
16
  */
8
- export const sendMessage = (message: WorkerRefreshTokenMessage, to: Worker) =>
9
- new Promise(function (resolve, reject) {
17
+ export const sendMessage = <T = any>(
18
+ message: WorkerRefreshTokenMessage | WorkerRevokeTokenMessage,
19
+ to: Worker
20
+ ): Promise<T> =>
21
+ new Promise<T>(function (resolve, reject) {
10
22
  const messageChannel = new MessageChannel();
11
23
 
12
24
  messageChannel.port1.onmessage = function (event) {
13
- // Only for fetch errors, as these get retried
14
25
  if (event.data.error) {
15
26
  reject(new Error(event.data.error));
16
27
  } else {