@auth0/auth0-spa-js 2.18.3 → 2.19.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.
@@ -1,4 +1,4 @@
1
- import { Auth0ClientOptions, RedirectLoginOptions, PopupLoginOptions, PopupConfigOptions, RedirectLoginResult, GetTokenSilentlyOptions, GetTokenWithPopupOptions, LogoutOptions, User, IdToken, GetTokenSilentlyVerboseResponse, TokenEndpointResponse, ConnectAccountRedirectResult, RedirectConnectAccountOptions, ClientConfiguration } from './global';
1
+ import { Auth0ClientOptions, RedirectLoginOptions, PopupLoginOptions, PopupConfigOptions, RedirectLoginResult, GetTokenSilentlyOptions, GetTokenWithPopupOptions, LogoutOptions, User, IdToken, GetTokenSilentlyVerboseResponse, TokenEndpointResponse, ConnectAccountRedirectResult, RedirectConnectAccountOptions, ClientConfiguration, RevokeRefreshTokenOptions } from './global';
2
2
  import { CustomTokenExchangeOptions } from './TokenExchange';
3
3
  import { Dpop } from './dpop/dpop';
4
4
  import { Fetcher, type FetcherConfig, type CustomFetchMinimalOutput } from './fetcher';
@@ -262,6 +262,43 @@ export declare class Auth0Client {
262
262
  * @param options
263
263
  */
264
264
  private _buildLogoutUrl;
265
+ /**
266
+ * ```js
267
+ * await auth0.revokeRefreshToken();
268
+ * ```
269
+ *
270
+ * Revokes the refresh token using the `/oauth/revoke` endpoint.
271
+ * This invalidates the refresh token so it can no longer be used to obtain new access tokens.
272
+ *
273
+ * The method works with both memory and localStorage cache modes:
274
+ * - For memory storage with worker: The refresh token never leaves the worker thread,
275
+ * maintaining security isolation
276
+ * - For localStorage: The token is retrieved from cache and revoked
277
+ *
278
+ * If `useRefreshTokens` is disabled, this method does nothing.
279
+ *
280
+ * **Important:** This method revokes the refresh token for a single audience. If your
281
+ * application requests tokens for multiple audiences, each audience may have its own
282
+ * refresh token. To fully revoke all refresh tokens, call this method once per audience.
283
+ * If you want to terminate the user's session entirely, use `logout()` instead.
284
+ *
285
+ * When using Multi-Resource Refresh Tokens (MRRT), a single refresh token may cover
286
+ * multiple audiences. In that case, revoking it will affect all cache entries that
287
+ * share the same token.
288
+ *
289
+ * @param options - Optional parameters to identify which refresh token to revoke.
290
+ * Defaults to the audience configured in `authorizationParams`.
291
+ *
292
+ * @example
293
+ * // Revoke the default refresh token
294
+ * await auth0.revokeRefreshToken();
295
+ *
296
+ * @example
297
+ * // Revoke refresh tokens for each audience individually
298
+ * await auth0.revokeRefreshToken({ audience: 'https://api.example.com' });
299
+ * await auth0.revokeRefreshToken({ audience: 'https://api2.example.com' });
300
+ */
301
+ revokeRefreshToken(options?: RevokeRefreshTokenOptions): Promise<void>;
265
302
  /**
266
303
  * ```js
267
304
  * await auth0.logout(options);
@@ -1,2 +1,33 @@
1
1
  import { TokenEndpointOptions, TokenEndpointResponse } from './global';
2
+ /**
3
+ * @ignore
4
+ * Internal options for the revokeToken API call.
5
+ * Kept in api.ts (not global.ts) so it is not part of the public type surface.
6
+ */
7
+ interface RevokeTokenOptions {
8
+ baseUrl: string;
9
+ /** Maps directly to the OAuth `client_id` parameter. */
10
+ client_id: string;
11
+ /** Tokens to revoke. Empty for the worker path — the worker holds its own store. */
12
+ refreshTokens: string[];
13
+ audience?: string;
14
+ timeout?: number;
15
+ auth0Client?: any;
16
+ useFormData?: boolean;
17
+ onRefreshTokenRevoked?: (refreshToken: string) => Promise<void> | void;
18
+ }
2
19
  export declare function oauthToken({ baseUrl, timeout, audience, scope, auth0Client, useFormData, useMrrt, dpop, ...options }: TokenEndpointOptions, worker?: Worker): Promise<TokenEndpointResponse>;
20
+ /**
21
+ * Revokes refresh tokens using the /oauth/revoke endpoint.
22
+ *
23
+ * Mirrors the oauthToken pattern: the worker/non-worker dispatch lives here,
24
+ * keeping Auth0Client free of transport concerns.
25
+ *
26
+ * - Worker path: sends a single message; the worker holds its own RT store and
27
+ * loops internally. refreshTokens is empty (worker ignores it).
28
+ * - Non-worker path: loops over refreshTokens and issues one request per token.
29
+ *
30
+ * @throws {GenericError} If any revoke request fails
31
+ */
32
+ export declare function revokeToken({ baseUrl, timeout, auth0Client, useFormData, refreshTokens, audience, client_id, onRefreshTokenRevoked }: RevokeTokenOptions, worker?: Worker): Promise<void>;
33
+ export {};
@@ -11,6 +11,7 @@ export declare class CacheManager {
11
11
  private modifiedCachedEntry;
12
12
  set(entry: CacheEntry): Promise<void>;
13
13
  remove(client_id: string, audience?: string, scope?: string): Promise<void>;
14
+ stripRefreshToken(refreshToken: string): Promise<void>;
14
15
  clear(clientId?: string): Promise<void>;
15
16
  private wrapCacheEntry;
16
17
  private getCacheKeys;
@@ -42,6 +43,18 @@ export declare class CacheManager {
42
43
  * @param allKeys A list of existing cache keys
43
44
  */
44
45
  private getEntryWithRefreshToken;
46
+ /**
47
+ * Returns all distinct refresh tokens stored for a given audience and client.
48
+ *
49
+ * Multiple cache entries may exist for the same audience when different scope
50
+ * combinations were obtained via separate authorization flows, each potentially
51
+ * carrying a different refresh token. A Set is used to deduplicate tokens that
52
+ * are shared across entries (e.g. MRRT).
53
+ *
54
+ * @param audience The audience to look up
55
+ * @param clientId The client id to scope the lookup
56
+ */
57
+ getRefreshTokensByAudience(audience: string, clientId: string): Promise<string[]>;
45
58
  /**
46
59
  * Updates the refresh token in all cache entries that contain the old refresh token.
47
60
  *
@@ -816,4 +816,11 @@ export type FetchResponse = {
816
816
  json: any;
817
817
  };
818
818
  export type GetTokenSilentlyVerboseResponse = Omit<TokenEndpointResponse, 'refresh_token'>;
819
+ /**
820
+ * Options for revoking a refresh token
821
+ */
822
+ export interface RevokeRefreshTokenOptions {
823
+ /** Audience to identify which refresh token to revoke. Omit for default audience. */
824
+ audience?: string;
825
+ }
819
826
  export type { Authenticator, AuthenticatorType, OobChannel, MfaFactorType, EnrollParams, EnrollOtpParams, EnrollSmsParams, EnrollVoiceParams, EnrollEmailParams, EnrollPushParams, EnrollmentResponse, OtpEnrollmentResponse, OobEnrollmentResponse, ChallengeAuthenticatorParams, ChallengeResponse, VerifyParams, MfaGrantType, EnrollmentFactor } from './mfa/types';
@@ -1,5 +1,11 @@
1
1
  import { FetchOptions } from './global';
2
2
  import { Dpop } from './dpop/dpop';
3
3
  export declare const createAbortController: () => AbortController;
4
+ /**
5
+ * Wraps a single `fetch` call with an AbortController-based timeout and
6
+ * returns the raw `Response`. Shared by the JSON token path and the revoke
7
+ * path to avoid duplicating abort/timeout orchestration.
8
+ */
9
+ export declare const fetchWithTimeout: (fetchUrl: string, fetchOptions: FetchOptions, timeout: number) => Promise<Response>;
4
10
  export declare const switchFetch: (fetchUrl: string, audience: string, scope: string, fetchOptions: FetchOptions, worker?: Worker, useFormData?: boolean, timeout?: number, useMrrt?: boolean) => Promise<any>;
5
11
  export declare function getJSON<T>(url: string, timeout: number | undefined, audience: string, scope: string, options: FetchOptions, worker?: Worker, useFormData?: boolean, useMrrt?: boolean, dpop?: Pick<Dpop, 'generateProof' | 'getNonce' | 'setNonce'>, isDpopRetry?: boolean): Promise<T>;
@@ -1,2 +1,2 @@
1
- declare const _default: "2.18.3";
1
+ declare const _default: "2.19.0";
2
2
  export default _default;
@@ -1,20 +1,27 @@
1
1
  import { FetchOptions } from '../global';
2
- /**
3
- * @ts-ignore
4
- */
5
2
  export type WorkerInitMessage = {
6
3
  type: 'init';
7
4
  allowedBaseUrl: string;
8
5
  };
9
- export type WorkerRefreshTokenMessage = {
6
+ type WorkerTokenMessage = {
10
7
  timeout: number;
11
8
  fetchUrl: string;
12
9
  fetchOptions: FetchOptions;
13
10
  useFormData?: boolean;
14
- useMrrt?: boolean;
15
11
  auth: {
16
12
  audience: string;
17
13
  scope: string;
18
14
  };
19
15
  };
20
- export type WorkerMessage = WorkerInitMessage | WorkerRefreshTokenMessage;
16
+ export type WorkerRefreshTokenMessage = WorkerTokenMessage & {
17
+ type: 'refresh';
18
+ useMrrt?: boolean;
19
+ };
20
+ export type WorkerRevokeTokenMessage = Omit<WorkerTokenMessage, 'auth'> & {
21
+ type: 'revoke';
22
+ auth: {
23
+ audience: string;
24
+ };
25
+ };
26
+ export type WorkerMessage = WorkerInitMessage | WorkerRefreshTokenMessage | WorkerRevokeTokenMessage;
27
+ export {};
@@ -1,7 +1,13 @@
1
- import { WorkerRefreshTokenMessage } from './worker.types';
1
+ import { WorkerRefreshTokenMessage, WorkerRevokeTokenMessage } from './worker.types';
2
2
  /**
3
- * Sends the specified message to the web worker
4
- * @param message The message to send
5
- * @param to The worker to send the message to
3
+ * Sends a message to a Web Worker and returns a Promise that resolves with
4
+ * the worker's response, or rejects if the worker replies with an error.
5
+ *
6
+ * Uses a {@link MessageChannel} so each call gets its own private reply port,
7
+ * making concurrent calls safe without shared state.
8
+ *
9
+ * @param message - The typed message to send (`refresh` or `revoke`).
10
+ * @param to - The target {@link Worker} instance.
11
+ * @returns A Promise that resolves with the worker's response payload.
6
12
  */
7
- export declare const sendMessage: (message: WorkerRefreshTokenMessage, to: Worker) => Promise<unknown>;
13
+ export declare const sendMessage: <T = any>(message: WorkerRefreshTokenMessage | WorkerRevokeTokenMessage, to: Worker) => Promise<T>;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "@auth0/auth0-spa-js",
4
4
  "description": "Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE",
5
5
  "license": "MIT",
6
- "version": "2.18.3",
6
+ "version": "2.19.0",
7
7
  "main": "dist/lib/auth0-spa-js.cjs.js",
8
8
  "types": "dist/typings/index.d.ts",
9
9
  "module": "dist/auth0-spa-js.production.esm.js",
@@ -93,7 +93,7 @@
93
93
  "rollup-plugin-livereload": "^2.0.5",
94
94
  "rollup-plugin-sourcemaps": "^0.6.3",
95
95
  "rollup-plugin-terser": "^7.0.2",
96
- "rollup-plugin-typescript2": "^0.36.0",
96
+ "rollup-plugin-typescript2": "^0.37.0",
97
97
  "rollup-plugin-visualizer": "^5.7.1",
98
98
  "rollup-plugin-web-worker-loader": "~1.6.1",
99
99
  "serve": "^14.0.1",
@@ -17,7 +17,7 @@ import {
17
17
 
18
18
  import { getLockManager, type ILockManager } from './lock';
19
19
 
20
- import { oauthToken } from './api';
20
+ import { oauthToken, revokeToken } from './api';
21
21
 
22
22
  import { injectDefaultScopes, scopesToRequest } from './scope';
23
23
 
@@ -90,7 +90,8 @@ import {
90
90
  RedirectConnectAccountOptions,
91
91
  ResponseType,
92
92
  ClientAuthorizationParams,
93
- ClientConfiguration
93
+ ClientConfiguration,
94
+ RevokeRefreshTokenOptions
94
95
  } from './global';
95
96
 
96
97
  // @ts-ignore
@@ -1144,6 +1145,76 @@ export class Auth0Client {
1144
1145
  return url + federatedQuery;
1145
1146
  }
1146
1147
 
1148
+ /**
1149
+ * ```js
1150
+ * await auth0.revokeRefreshToken();
1151
+ * ```
1152
+ *
1153
+ * Revokes the refresh token using the `/oauth/revoke` endpoint.
1154
+ * This invalidates the refresh token so it can no longer be used to obtain new access tokens.
1155
+ *
1156
+ * The method works with both memory and localStorage cache modes:
1157
+ * - For memory storage with worker: The refresh token never leaves the worker thread,
1158
+ * maintaining security isolation
1159
+ * - For localStorage: The token is retrieved from cache and revoked
1160
+ *
1161
+ * If `useRefreshTokens` is disabled, this method does nothing.
1162
+ *
1163
+ * **Important:** This method revokes the refresh token for a single audience. If your
1164
+ * application requests tokens for multiple audiences, each audience may have its own
1165
+ * refresh token. To fully revoke all refresh tokens, call this method once per audience.
1166
+ * If you want to terminate the user's session entirely, use `logout()` instead.
1167
+ *
1168
+ * When using Multi-Resource Refresh Tokens (MRRT), a single refresh token may cover
1169
+ * multiple audiences. In that case, revoking it will affect all cache entries that
1170
+ * share the same token.
1171
+ *
1172
+ * @param options - Optional parameters to identify which refresh token to revoke.
1173
+ * Defaults to the audience configured in `authorizationParams`.
1174
+ *
1175
+ * @example
1176
+ * // Revoke the default refresh token
1177
+ * await auth0.revokeRefreshToken();
1178
+ *
1179
+ * @example
1180
+ * // Revoke refresh tokens for each audience individually
1181
+ * await auth0.revokeRefreshToken({ audience: 'https://api.example.com' });
1182
+ * await auth0.revokeRefreshToken({ audience: 'https://api2.example.com' });
1183
+ */
1184
+ public async revokeRefreshToken(options: RevokeRefreshTokenOptions = {}): Promise<void> {
1185
+ if (!this.options.useRefreshTokens) {
1186
+ return;
1187
+ }
1188
+
1189
+ const audience =
1190
+ options.audience || this.options.authorizationParams.audience;
1191
+
1192
+ const resolvedAudience = audience || DEFAULT_AUDIENCE;
1193
+
1194
+ // For the non-worker path the main-thread cache holds the refresh tokens.
1195
+ // For the worker path the worker holds its own RT store — the cache returns
1196
+ // [] and revokeToken sends a single message; the worker loops internally.
1197
+ const refreshTokens = await this.cacheManager.getRefreshTokensByAudience(
1198
+ resolvedAudience,
1199
+ this.options.clientId
1200
+ );
1201
+
1202
+ await revokeToken(
1203
+ {
1204
+ baseUrl: this.domainUrl,
1205
+ timeout: this.httpTimeoutMs,
1206
+ auth0Client: this.options.auth0Client,
1207
+ useFormData: this.options.useFormData,
1208
+ client_id: this.options.clientId,
1209
+ refreshTokens,
1210
+ audience: resolvedAudience,
1211
+ onRefreshTokenRevoked: refreshToken =>
1212
+ this.cacheManager.stripRefreshToken(refreshToken)
1213
+ },
1214
+ this.worker
1215
+ );
1216
+ }
1217
+
1147
1218
  /**
1148
1219
  * ```js
1149
1220
  * await auth0.logout(options);
package/src/api.ts CHANGED
@@ -1,7 +1,31 @@
1
1
  import { TokenEndpointOptions, TokenEndpointResponse } from './global';
2
- import { DEFAULT_AUTH0_CLIENT, DEFAULT_AUDIENCE } from './constants';
2
+ import {
3
+ DEFAULT_AUTH0_CLIENT,
4
+ DEFAULT_AUDIENCE,
5
+ DEFAULT_FETCH_TIMEOUT_MS
6
+ } from './constants';
7
+
8
+ /**
9
+ * @ignore
10
+ * Internal options for the revokeToken API call.
11
+ * Kept in api.ts (not global.ts) so it is not part of the public type surface.
12
+ */
13
+ interface RevokeTokenOptions {
14
+ baseUrl: string;
15
+ /** Maps directly to the OAuth `client_id` parameter. */
16
+ client_id: string;
17
+ /** Tokens to revoke. Empty for the worker path — the worker holds its own store. */
18
+ refreshTokens: string[];
19
+ audience?: string;
20
+ timeout?: number;
21
+ auth0Client?: any;
22
+ useFormData?: boolean;
23
+ onRefreshTokenRevoked?: (refreshToken: string) => Promise<void> | void;
24
+ }
3
25
  import * as dpopUtils from './dpop/utils';
4
- import { getJSON } from './http';
26
+ import { GenericError } from './errors';
27
+ import { getJSON, fetchWithTimeout } from './http';
28
+ import { sendMessage } from './worker/worker.utils';
5
29
  import { createQueryParams, stripAuth0Client } from './utils';
6
30
 
7
31
  export async function oauthToken(
@@ -59,3 +83,89 @@ export async function oauthToken(
59
83
  isDpopSupported ? dpop : undefined
60
84
  );
61
85
  }
86
+
87
+ /**
88
+ * Revokes refresh tokens using the /oauth/revoke endpoint.
89
+ *
90
+ * Mirrors the oauthToken pattern: the worker/non-worker dispatch lives here,
91
+ * keeping Auth0Client free of transport concerns.
92
+ *
93
+ * - Worker path: sends a single message; the worker holds its own RT store and
94
+ * loops internally. refreshTokens is empty (worker ignores it).
95
+ * - Non-worker path: loops over refreshTokens and issues one request per token.
96
+ *
97
+ * @throws {GenericError} If any revoke request fails
98
+ */
99
+ export async function revokeToken(
100
+ {
101
+ baseUrl,
102
+ timeout,
103
+ auth0Client,
104
+ useFormData,
105
+ refreshTokens,
106
+ audience,
107
+ client_id,
108
+ onRefreshTokenRevoked
109
+ }: RevokeTokenOptions,
110
+ worker?: Worker
111
+ ): Promise<void> {
112
+ const resolvedTimeout = timeout || DEFAULT_FETCH_TIMEOUT_MS;
113
+ // token_type_hint is a SHOULD per RFC 7009 §2.1; used in both paths below.
114
+ const token_type_hint = 'refresh_token' as const;
115
+ const fetchUrl = `${baseUrl}/oauth/revoke`;
116
+ const headers = {
117
+ 'Content-Type': useFormData
118
+ ? 'application/x-www-form-urlencoded'
119
+ : 'application/json',
120
+ 'Auth0-Client': btoa(
121
+ JSON.stringify(stripAuth0Client(auth0Client || DEFAULT_AUTH0_CLIENT))
122
+ )
123
+ };
124
+
125
+ if (worker) {
126
+ // Worker holds its own RT store and injects each token into the request.
127
+ // Send the base body (without token) so the worker can loop over its tokens.
128
+ const baseParams = { client_id, token_type_hint };
129
+ const body = useFormData
130
+ ? createQueryParams(baseParams)
131
+ : JSON.stringify(baseParams);
132
+
133
+ return sendMessage(
134
+ {
135
+ type: 'revoke',
136
+ timeout: resolvedTimeout,
137
+ fetchUrl,
138
+ fetchOptions: { method: 'POST', body, headers },
139
+ useFormData,
140
+ auth: { audience: audience ?? DEFAULT_AUDIENCE }
141
+ },
142
+ worker
143
+ );
144
+ }
145
+
146
+ for (const refreshToken of refreshTokens) {
147
+ const params = { client_id, token_type_hint, token: refreshToken };
148
+ const body = useFormData
149
+ ? createQueryParams(params)
150
+ : JSON.stringify(params);
151
+
152
+ const response = await fetchWithTimeout(
153
+ fetchUrl,
154
+ { method: 'POST', body, headers },
155
+ resolvedTimeout
156
+ );
157
+
158
+ if (!response.ok) {
159
+ let error: string | undefined;
160
+ let errorDescription: string | undefined;
161
+ try {
162
+ ({ error, error_description: errorDescription } = JSON.parse(await response.text()));
163
+ } catch {
164
+ // body absent or not valid JSON
165
+ }
166
+ throw new GenericError(error || 'revoke_error', errorDescription || `HTTP error ${response.status}`);
167
+ }
168
+
169
+ await onRefreshTokenRevoked?.(refreshToken);
170
+ }
171
+ }
@@ -163,6 +163,23 @@ export class CacheManager {
163
163
  await this.cache.remove(cacheKey.toKey());
164
164
  }
165
165
 
166
+ async stripRefreshToken(refreshToken: string): Promise<void> {
167
+ const keys = await this.getCacheKeys();
168
+
169
+ /* c8 ignore next */
170
+ if (!keys) return;
171
+
172
+ // Find all cache entries that have this refresh token and strip only the refresh token,
173
+ // leaving the access token intact (it remains valid until it expires)
174
+ for (const key of keys) {
175
+ const entry = await this.cache.get<WrappedCacheEntry>(key);
176
+ if (entry?.body?.refresh_token === refreshToken) {
177
+ delete entry.body.refresh_token;
178
+ await this.cache.set(key, entry);
179
+ }
180
+ }
181
+ }
182
+
166
183
  async clear(clientId?: string): Promise<void> {
167
184
  const keys = await this.getCacheKeys();
168
185
 
@@ -269,6 +286,46 @@ export class CacheManager {
269
286
  return undefined;
270
287
  }
271
288
 
289
+ /**
290
+ * Returns all distinct refresh tokens stored for a given audience and client.
291
+ *
292
+ * Multiple cache entries may exist for the same audience when different scope
293
+ * combinations were obtained via separate authorization flows, each potentially
294
+ * carrying a different refresh token. A Set is used to deduplicate tokens that
295
+ * are shared across entries (e.g. MRRT).
296
+ *
297
+ * @param audience The audience to look up
298
+ * @param clientId The client id to scope the lookup
299
+ */
300
+ async getRefreshTokensByAudience(
301
+ audience: string,
302
+ clientId: string
303
+ ): Promise<string[]> {
304
+ const keys = await this.getCacheKeys();
305
+
306
+ if (!keys) return [];
307
+
308
+ const tokens = new Set<string>();
309
+
310
+ for (const key of keys) {
311
+ const cacheKey = CacheKey.fromKey(key);
312
+
313
+ if (
314
+ cacheKey.prefix === CACHE_KEY_PREFIX &&
315
+ cacheKey.clientId === clientId &&
316
+ cacheKey.audience === audience
317
+ ) {
318
+ const entry = await this.cache.get<WrappedCacheEntry>(key);
319
+
320
+ if (entry?.body?.refresh_token) {
321
+ tokens.add(entry.body.refresh_token);
322
+ }
323
+ }
324
+ }
325
+
326
+ return Array.from(tokens);
327
+ }
328
+
272
329
  /**
273
330
  * Updates the refresh token in all cache entries that contain the old refresh token.
274
331
  *
package/src/global.ts CHANGED
@@ -903,6 +903,14 @@ export type GetTokenSilentlyVerboseResponse = Omit<
903
903
  'refresh_token'
904
904
  >;
905
905
 
906
+ /**
907
+ * Options for revoking a refresh token
908
+ */
909
+ export interface RevokeRefreshTokenOptions {
910
+ /** Audience to identify which refresh token to revoke. Omit for default audience. */
911
+ audience?: string;
912
+ }
913
+
906
914
  // MFA API types
907
915
  export type {
908
916
  Authenticator,
package/src/http.ts CHANGED
@@ -17,27 +17,16 @@ import { DPOP_NONCE_HEADER } from './dpop/utils';
17
17
 
18
18
  export const createAbortController = () => new AbortController();
19
19
 
20
- const dofetch = async (fetchUrl: string, fetchOptions: FetchOptions) => {
21
- const response = await fetch(fetchUrl, fetchOptions);
22
-
23
- return {
24
- ok: response.ok,
25
- json: await response.json(),
26
-
27
- /**
28
- * This is not needed, but do it anyway so the object shape is the
29
- * same as when using a Web Worker (which *does* need this, see
30
- * src/worker/token.worker.ts).
31
- */
32
- headers: fromEntries(response.headers)
33
- };
34
- };
35
-
36
- const fetchWithoutWorker = async (
20
+ /**
21
+ * Wraps a single `fetch` call with an AbortController-based timeout and
22
+ * returns the raw `Response`. Shared by the JSON token path and the revoke
23
+ * path to avoid duplicating abort/timeout orchestration.
24
+ */
25
+ export const fetchWithTimeout = (
37
26
  fetchUrl: string,
38
27
  fetchOptions: FetchOptions,
39
28
  timeout: number
40
- ) => {
29
+ ): Promise<Response> => {
41
30
  const controller = createAbortController();
42
31
  fetchOptions.signal = controller.signal;
43
32
 
@@ -45,9 +34,8 @@ const fetchWithoutWorker = async (
45
34
 
46
35
  // The promise will resolve with one of these two promises (the fetch or the timeout), whichever completes first.
47
36
  return Promise.race([
48
- dofetch(fetchUrl, fetchOptions),
49
-
50
- new Promise((_, reject) => {
37
+ fetch(fetchUrl, fetchOptions),
38
+ new Promise<never>((_, reject) => {
51
39
  timeoutId = setTimeout(() => {
52
40
  controller.abort();
53
41
  reject(new Error("Timeout when executing 'fetch'"));
@@ -58,6 +46,24 @@ const fetchWithoutWorker = async (
58
46
  });
59
47
  };
60
48
 
49
+ const fetchWithoutWorker = async (
50
+ fetchUrl: string,
51
+ fetchOptions: FetchOptions,
52
+ timeout: number
53
+ ) => {
54
+ const response = await fetchWithTimeout(fetchUrl, fetchOptions, timeout);
55
+ return {
56
+ ok: response.ok,
57
+ json: await response.json(),
58
+ /**
59
+ * This is not needed, but do it anyway so the object shape is the
60
+ * same as when using a Web Worker (which *does* need this, see
61
+ * src/worker/token.worker.ts).
62
+ */
63
+ headers: fromEntries(response.headers)
64
+ };
65
+ };
66
+
61
67
  const fetchWithWorker = async (
62
68
  fetchUrl: string,
63
69
  audience: string,
@@ -70,6 +76,7 @@ const fetchWithWorker = async (
70
76
  ) => {
71
77
  return sendMessage(
72
78
  {
79
+ type: 'refresh',
73
80
  auth: {
74
81
  audience,
75
82
  scope
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '2.18.3';
1
+ export default '2.19.0';