@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.
- package/README.md +1 -1
- package/dist/auth0-spa-js.development.js +405 -362
- package/dist/auth0-spa-js.development.js.map +1 -1
- package/dist/auth0-spa-js.production.esm.js +1 -1
- package/dist/auth0-spa-js.production.esm.js.map +1 -1
- package/dist/auth0-spa-js.production.js +1 -1
- package/dist/auth0-spa-js.production.js.map +1 -1
- package/dist/auth0-spa-js.worker.development.js +132 -81
- package/dist/auth0-spa-js.worker.development.js.map +1 -1
- package/dist/auth0-spa-js.worker.production.js +1 -1
- package/dist/auth0-spa-js.worker.production.js.map +1 -1
- package/dist/lib/auth0-spa-js.cjs.js +427 -385
- package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
- package/dist/typings/Auth0Client.d.ts +38 -1
- package/dist/typings/api.d.ts +31 -0
- package/dist/typings/cache/cache-manager.d.ts +13 -0
- package/dist/typings/global.d.ts +7 -0
- package/dist/typings/http.d.ts +6 -0
- package/dist/typings/version.d.ts +1 -1
- package/dist/typings/worker/worker.types.d.ts +13 -6
- package/dist/typings/worker/worker.utils.d.ts +11 -5
- package/package.json +2 -2
- package/src/Auth0Client.ts +73 -2
- package/src/api.ts +112 -2
- package/src/cache/cache-manager.ts +57 -0
- package/src/global.ts +8 -0
- package/src/http.ts +28 -21
- package/src/version.ts +1 -1
- package/src/worker/token.worker.ts +120 -5
- package/src/worker/worker.types.ts +17 -6
- 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 {
|
|
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 ===
|
|
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
|
-
|
|
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
|
|
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 {
|
|
1
|
+
import {
|
|
2
|
+
WorkerRefreshTokenMessage,
|
|
3
|
+
WorkerRevokeTokenMessage
|
|
4
|
+
} from './worker.types';
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
|
-
* Sends
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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 =
|
|
9
|
-
|
|
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 {
|