@inrupt/solid-client-authn-browser 1.11.7 → 1.11.8-alpha.2

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/dist/ClientAuthentication.d.ts +18 -17
  2. package/dist/Session.d.ts +35 -36
  3. package/dist/Session.js +2 -45
  4. package/dist/Session.js.map +1 -1
  5. package/dist/constant.d.ts +2 -2
  6. package/dist/defaultSession.d.ts +9 -9
  7. package/dist/dependencies.d.ts +6 -6
  8. package/dist/iframe.d.ts +3 -3
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.es.js +1693 -0
  11. package/dist/index.js +1717 -27
  12. package/dist/login/AggregateLoginHandler.d.ts +4 -4
  13. package/dist/login/oidc/AggregateOidcHandler.d.ts +4 -4
  14. package/dist/login/oidc/ClientRegistrar.d.ts +6 -6
  15. package/dist/login/oidc/IssuerConfigFetcher.d.ts +8 -8
  16. package/dist/login/oidc/OidcLoginHandler.d.ts +10 -10
  17. package/dist/login/oidc/Redirector.d.ts +4 -4
  18. package/dist/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.d.ts +8 -8
  19. package/dist/login/oidc/oidcHandlers/OidcHandlerCanHandleTest.d.ts +9 -9
  20. package/dist/login/oidc/redirectHandler/AggregateRedirectHandler.d.ts +4 -4
  21. package/dist/login/oidc/redirectHandler/AuthCodeRedirectHandler.d.ts +15 -16
  22. package/dist/login/oidc/redirectHandler/AuthCodeRedirectHandler.js +1 -23
  23. package/dist/login/oidc/redirectHandler/AuthCodeRedirectHandler.js.map +1 -1
  24. package/dist/login/oidc/redirectHandler/ErrorOidcHandler.d.ts +9 -9
  25. package/dist/login/oidc/redirectHandler/FallbackRedirectHandler.d.ts +7 -7
  26. package/dist/login/oidc/refresh/TokenRefresher.d.ts +10 -10
  27. package/dist/login/webid/WebIdLoginHandler.d.ts +5 -5
  28. package/dist/logout/GeneralLogoutHandler.d.ts +7 -7
  29. package/dist/sessionInfo/SessionInfoManager.d.ts +16 -16
  30. package/dist/sessionInfo/SessionInfoManager.js +0 -8
  31. package/dist/sessionInfo/SessionInfoManager.js.map +1 -1
  32. package/dist/solid-client-authn.bundle.js +1720 -2
  33. package/dist/storage/BrowserStorage.d.ts +7 -7
  34. package/dist/storage/StorageUtility.d.ts +4 -4
  35. package/dist/util/UuidGenerator.d.ts +6 -6
  36. package/dist/util/urlPath.d.ts +1 -1
  37. package/examples/demoClientApp/package-lock.json +85 -18
  38. package/examples/demoClientApp/package.json +2 -1
  39. package/examples/demoClientApp/src/DemoClientApp.js +9 -1
  40. package/examples/single/bundle/package-lock.json +18 -18
  41. package/jest.config.js +6 -1
  42. package/package.json +17 -29
  43. package/rollup.config.js +57 -0
  44. package/src/ClientAuthentication.spec.ts +69 -1
  45. package/src/ClientAuthentication.ts +32 -14
  46. package/src/Session.spec.ts +0 -224
  47. package/src/Session.ts +8 -88
  48. package/src/login/oidc/__mocks__/Redirector.ts +9 -9
  49. package/src/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.spec.ts +34 -18
  50. package/src/login/oidc/redirectHandler/AuthCodeRedirectHandler.spec.ts +3 -171
  51. package/src/login/oidc/redirectHandler/AuthCodeRedirectHandler.ts +0 -68
  52. package/src/login/oidc/redirectHandler/__mocks__/RedirectHandler.ts +13 -0
  53. package/src/sessionInfo/SessionInfoManager.spec.ts +0 -27
  54. package/src/sessionInfo/SessionInfoManager.ts +0 -10
  55. package/src/index.browser.spec.ts +0 -27
  56. package/src/index.browser.ts +0 -24
  57. package/webpack.browser.js +0 -19
  58. package/webpack.common.js +0 -22
  59. package/webpack.config.js +0 -8
  60. package/webpack.dev.js +0 -17
  61. package/webpack.prod.js +0 -3
@@ -33,6 +33,7 @@ import {
33
33
  IIssuerConfigFetcher,
34
34
  ISessionInternalInfo,
35
35
  ILoginOptions,
36
+ EVENTS,
36
37
  } from "@inrupt/solid-client-authn-core";
37
38
  import { removeOidcQueryParam } from "@inrupt/oidc-client-ext";
38
39
  import { EventEmitter } from "events";
@@ -129,13 +130,37 @@ export default class ClientAuthentication {
129
130
  url: string,
130
131
  eventEmitter: EventEmitter
131
132
  ): Promise<ISessionInfo | undefined> => {
132
- const redirectInfo = await this.redirectHandler.handle(url, eventEmitter);
133
- // The `FallbackRedirectHandler` directly returns the global `fetch` for
134
- // his value, so we should ensure it's bound to `window` rather than to
135
- // ClientAuthentication, to avoid the following error:
136
- // > 'fetch' called on an object that does not implement interface Window.
137
- this.fetch = redirectInfo.fetch.bind(window);
133
+ try {
134
+ const redirectInfo = await this.redirectHandler.handle(url, eventEmitter);
135
+ // The `FallbackRedirectHandler` directly returns the global `fetch` for
136
+ // his value, so we should ensure it's bound to `window` rather than to
137
+ // ClientAuthentication, to avoid the following error:
138
+ // > 'fetch' called on an object that does not implement interface Window.
139
+ this.fetch = redirectInfo.fetch.bind(window);
140
+
141
+ // Strip the oauth params:
142
+ this.cleanUrlAfterRedirect(url);
143
+
144
+ return {
145
+ isLoggedIn: redirectInfo.isLoggedIn,
146
+ webId: redirectInfo.webId,
147
+ sessionId: redirectInfo.sessionId,
148
+ expirationDate: redirectInfo.expirationDate,
149
+ };
150
+ } catch (err) {
151
+ // Strip the oauth params:
152
+ this.cleanUrlAfterRedirect(url);
153
+
154
+ // FIXME: EVENTS.ERROR should be errorCode, errorDescription
155
+ //
156
+ // I'm not sure if "redirect" is a good error code, and in theory `err`
157
+ // maybe an Error object and not a string; Maybe we want to just hardcode
158
+ // a description instead?
159
+ eventEmitter.emit(EVENTS.ERROR, "redirect", err);
160
+ }
161
+ };
138
162
 
163
+ private cleanUrlAfterRedirect(url: string): void {
139
164
  const cleanedUpUrl = new URL(url);
140
165
  cleanedUpUrl.searchParams.delete("state");
141
166
  // For auth code flow
@@ -153,12 +178,5 @@ export default class ClientAuthentication {
153
178
  // authentication library will be called again with what are now invalid
154
179
  // query parameters!).
155
180
  window.history.replaceState(null, "", cleanedUpUrl.toString());
156
-
157
- return {
158
- isLoggedIn: redirectInfo.isLoggedIn,
159
- webId: redirectInfo.webId,
160
- sessionId: redirectInfo.sessionId,
161
- expirationDate: redirectInfo.expirationDate,
162
- };
163
- };
181
+ }
164
182
  }
@@ -395,230 +395,6 @@ describe("Session", () => {
395
395
  expect(incomingRedirectHandler).toHaveBeenCalled();
396
396
  });
397
397
 
398
- // This workaround will be removed after we've settled on an API that allows us to silently
399
- // re-activate the user's session after a refresh:
400
- describe("(using the temporary workaround for losing a session after refresh)", () => {
401
- it("does not report the user to be logged in when the workaround cookie is not present, and just executes the redirect", async () => {
402
- const clientAuthentication = mockClientAuthentication();
403
- clientAuthentication.handleIncomingRedirect = jest.fn(
404
- async (_url: string) => undefined
405
- );
406
- const mySession = new Session({ clientAuthentication });
407
- await mySession.handleIncomingRedirect({
408
- url: "https://some.url",
409
- useEssSession: true,
410
- });
411
- expect(mySession.info.isLoggedIn).toBe(false);
412
- expect(mySession.info.webId).toBeUndefined();
413
- expect(
414
- clientAuthentication.handleIncomingRedirect
415
- ).toHaveBeenCalledTimes(1);
416
- // The workaround should be enabled by default
417
- expect(
418
- window.localStorage.getItem("tmp-resource-server-session-enabled")
419
- ).toBe("true");
420
- });
421
-
422
- it("re-initialises the user's WebID without redirection if the proper cookie is set", async () => {
423
- mockLocalStorage({
424
- "tmp-resource-server-session-info": JSON.stringify({
425
- webId: "https://my.pod/profile#me",
426
- sessions: {
427
- "https://my.pod/": { expiration: 9000000000000 },
428
- },
429
- }),
430
- });
431
- const clientAuthentication = mockClientAuthentication();
432
- clientAuthentication.handleIncomingRedirect = jest.fn();
433
- const mySession = new Session({ clientAuthentication });
434
- await mySession.handleIncomingRedirect({
435
- url: "https://some.url",
436
- useEssSession: true,
437
- });
438
- expect(mySession.info.isLoggedIn).toBe(true);
439
- expect(mySession.info.webId).toBe("https://my.pod/profile#me");
440
- expect(
441
- clientAuthentication.handleIncomingRedirect
442
- ).toHaveBeenCalledTimes(0);
443
- // The workaround should be enabled by default
444
- expect(
445
- window.localStorage.getItem("tmp-resource-server-session-enabled")
446
- ).toBe("true");
447
- });
448
-
449
- it("does not attempt to use the workaround if the long-term solution (silent refresh) is enabled", async () => {
450
- mockLocalStorage({
451
- "tmp-resource-server-session-info": JSON.stringify({
452
- webId: "https://my.pod/profile#me",
453
- sessions: {
454
- "https://my.pod/": { expiration: 9000000000000 },
455
- },
456
- }),
457
- });
458
- const clientAuthentication = mockClientAuthentication();
459
- clientAuthentication.handleIncomingRedirect = jest.fn();
460
- const mySession = new Session({ clientAuthentication });
461
- await mySession.handleIncomingRedirect({
462
- url: "https://some.url",
463
- restorePreviousSession: true,
464
- });
465
- expect(mySession.info.isLoggedIn).toBe(false);
466
- expect(mySession.info.webId).toBeUndefined();
467
- expect(
468
- clientAuthentication.handleIncomingRedirect
469
- ).toHaveBeenCalledTimes(1);
470
- expect(
471
- window.localStorage.getItem("tmp-resource-server-session-enabled")
472
- ).toBe("false");
473
- });
474
-
475
- it("overrides the workaround if both silent refresh and the ESS session are enabled", async () => {
476
- mockLocalStorage({
477
- "tmp-resource-server-session-info": JSON.stringify({
478
- webId: "https://my.pod/profile#me",
479
- sessions: {
480
- "https://my.pod/": { expiration: 9000000000000 },
481
- },
482
- }),
483
- });
484
- const clientAuthentication = mockClientAuthentication();
485
- clientAuthentication.handleIncomingRedirect = jest.fn();
486
- const mySession = new Session({ clientAuthentication });
487
- await mySession.handleIncomingRedirect({
488
- url: "https://some.url",
489
- restorePreviousSession: true,
490
- useEssSession: true,
491
- });
492
- expect(mySession.info.isLoggedIn).toBe(false);
493
- expect(mySession.info.webId).toBeUndefined();
494
- expect(
495
- clientAuthentication.handleIncomingRedirect
496
- ).toHaveBeenCalledTimes(1);
497
- expect(
498
- window.localStorage.getItem("tmp-resource-server-session-enabled")
499
- ).toBe("false");
500
- });
501
-
502
- it("does not attempt to use the workaround if it is not explicitly enabled", async () => {
503
- mockLocalStorage({
504
- "tmp-resource-server-session-info": JSON.stringify({
505
- webId: "https://my.pod/profile#me",
506
- sessions: {
507
- "https://my.pod/": { expiration: 9000000000000 },
508
- },
509
- }),
510
- });
511
- const clientAuthentication = mockClientAuthentication();
512
- clientAuthentication.handleIncomingRedirect = jest.fn();
513
- const mySession = new Session({ clientAuthentication });
514
- await mySession.handleIncomingRedirect({
515
- url: "https://some.url",
516
- });
517
- expect(mySession.info.isLoggedIn).toBe(false);
518
- expect(mySession.info.webId).toBeUndefined();
519
- expect(
520
- clientAuthentication.handleIncomingRedirect
521
- ).toHaveBeenCalledTimes(1);
522
- expect(
523
- window.localStorage.getItem("tmp-resource-server-session-enabled")
524
- ).toBe("false");
525
- });
526
-
527
- it("does not attempt to use the workaround if it is explicitly disabled", async () => {
528
- mockLocalStorage({
529
- "tmp-resource-server-session-info": JSON.stringify({
530
- webId: "https://my.pod/profile#me",
531
- sessions: {
532
- "https://my.pod/": { expiration: 9000000000000 },
533
- },
534
- }),
535
- });
536
- const clientAuthentication = mockClientAuthentication();
537
- clientAuthentication.handleIncomingRedirect = jest.fn();
538
- const mySession = new Session({ clientAuthentication });
539
- await mySession.handleIncomingRedirect({
540
- url: "https://some.url",
541
- useEssSession: false,
542
- });
543
- expect(mySession.info.isLoggedIn).toBe(false);
544
- expect(mySession.info.webId).toBeUndefined();
545
- expect(
546
- clientAuthentication.handleIncomingRedirect
547
- ).toHaveBeenCalledTimes(1);
548
- expect(
549
- window.localStorage.getItem("tmp-resource-server-session-enabled")
550
- ).toBe("false");
551
- });
552
-
553
- it("does not mark an almost-expired session as logged in", async () => {
554
- mockLocalStorage({
555
- "tmp-resource-server-session-info": JSON.stringify({
556
- webId: "https://my.pod/profile#me",
557
- sessions: {
558
- "https://my.pod/": { expiration: Date.now() + 10000 },
559
- },
560
- }),
561
- });
562
-
563
- const clientAuthentication = mockClientAuthentication();
564
- clientAuthentication.handleIncomingRedirect = jest.fn();
565
- const mySession = new Session({ clientAuthentication });
566
- await mySession.handleIncomingRedirect({
567
- url: "https://some.url",
568
- useEssSession: true,
569
- });
570
- expect(mySession.info.isLoggedIn).toBe(false);
571
- expect(mySession.info.webId).toBeUndefined();
572
- expect(
573
- clientAuthentication.handleIncomingRedirect
574
- ).toHaveBeenCalledTimes(1);
575
- });
576
-
577
- it("logs you in even if your Solid Identity Provider is not your Resource server", async () => {
578
- mockLocalStorage({
579
- "tmp-resource-server-session-info": JSON.stringify({
580
- webId: "https://my.pod/profile#me",
581
- sessions: {
582
- "https://not.my.pod/": { expiration: 9000000000000 },
583
- },
584
- }),
585
- });
586
- const clientAuthentication = mockClientAuthentication();
587
- clientAuthentication.handleIncomingRedirect = jest.fn();
588
- const mySession = new Session({ clientAuthentication });
589
- await mySession.handleIncomingRedirect({
590
- url: "https://some.url",
591
- useEssSession: true,
592
- });
593
- expect(mySession.info.isLoggedIn).toBe(true);
594
- expect(mySession.info.webId).toBe("https://my.pod/profile#me");
595
- expect(
596
- clientAuthentication.handleIncomingRedirect
597
- ).toHaveBeenCalledTimes(0);
598
- });
599
-
600
- it("does not log the user in if the data in the storage was invalid", async () => {
601
- mockLocalStorage({
602
- "tmp-resource-server-session-info": JSON.stringify({
603
- webId: "https://my.pod/profile#me",
604
- // `sessions` key is intentionally missing
605
- }),
606
- });
607
- const clientAuthentication = mockClientAuthentication();
608
- clientAuthentication.handleIncomingRedirect = jest.fn();
609
- const mySession = new Session({ clientAuthentication });
610
- await mySession.handleIncomingRedirect({
611
- url: "https://some.url",
612
- useEssSession: true,
613
- });
614
- expect(mySession.info.isLoggedIn).toBe(false);
615
- expect(mySession.info.webId).toBeUndefined();
616
- expect(
617
- clientAuthentication.handleIncomingRedirect
618
- ).toHaveBeenCalledTimes(1);
619
- });
620
- });
621
-
622
398
  it("posts the redirect IRI to the parent if in an iframe", async () => {
623
399
  // Pretend we are in an iframe.
624
400
  const frameElement = jest.spyOn(window, "frameElement", "get");
package/src/Session.ts CHANGED
@@ -28,7 +28,6 @@ import {
28
28
  ILoginInputOptions,
29
29
  ISessionInfo,
30
30
  IStorage,
31
- ResourceServerSession,
32
31
  } from "@inrupt/solid-client-authn-core";
33
32
  import { v4 } from "uuid";
34
33
  import ClientAuthentication from "./ClientAuthentication";
@@ -83,6 +82,8 @@ export interface IHandleIncomingRedirectOptions {
83
82
  * `useEssSession` defaults to false and will be removed in the future; to
84
83
  * preserve sessions across page reloads, use of `restorePreviousSession` is
85
84
  * recommended.
85
+ *
86
+ * @deprecated unreleased
86
87
  */
87
88
  useEssSession?: boolean;
88
89
  /**
@@ -148,9 +149,6 @@ export class Session extends EventEmitter {
148
149
 
149
150
  private tokenRequestInProgress = false;
150
151
 
151
- // Remove this when removing the `useEssSession` workaround:
152
- private tmpFetchWithCookies = false;
153
-
154
152
  /**
155
153
  * Session object constructor. Typically called as follows:
156
154
  *
@@ -237,6 +235,11 @@ export class Session extends EventEmitter {
237
235
  this.on(EVENTS.SESSION_EXPIRED, () => this.internalLogout(false));
238
236
 
239
237
  this.on(EVENTS.ERROR, () => this.internalLogout(false));
238
+
239
+ // @deprecated: Remove this when removing all support for the useEssSession parameter.
240
+ // This intends at cleaning up local storage of applications from data related
241
+ // to the resource server session workaround.
242
+ window.localStorage.removeItem("tmp-resource-server-session-enabled");
240
243
  }
241
244
 
242
245
  /**
@@ -271,14 +274,7 @@ export class Session extends EventEmitter {
271
274
  * @param init Optional parameters customizing the request, by specifying an HTTP method, headers, a body, etc. Follows the [WHATWG Fetch Standard](https://fetch.spec.whatwg.org/).
272
275
  */
273
276
  fetch = async (url: RequestInfo, init?: RequestInit): Promise<Response> => {
274
- return this.clientAuthentication.fetch(url, {
275
- ...init,
276
- credentials: this.tmpFetchWithCookies
277
- ? /* istanbul ignore next Simulating a cookie-enabled session in tests is too much work for a temporary, disabled-by-default workaround: */
278
- "include"
279
- : /* istanbul ignore next Simulating a cookie-enabled session in tests is too much work for a temporary, disabled-by-default workaround: */
280
- init?.credentials,
281
- });
277
+ return this.clientAuthentication.fetch(url, init);
282
278
  };
283
279
 
284
280
  /**
@@ -295,7 +291,6 @@ export class Session extends EventEmitter {
295
291
  window.localStorage.removeItem(KEY_CURRENT_SESSION);
296
292
  await this.clientAuthentication.logout(this.info.sessionId);
297
293
  this.info.isLoggedIn = false;
298
- this.tmpFetchWithCookies = false;
299
294
  if (emitSignal) {
300
295
  this.emit(EVENTS.LOGOUT);
301
296
  }
@@ -333,81 +328,6 @@ export class Session extends EventEmitter {
333
328
  return undefined;
334
329
  }
335
330
 
336
- // Unfortunately, regular sessions are lost when the user refreshes the page or opens a new tab.
337
- // While we're figuring out the API for a longer-term solution, as a temporary workaround some
338
- // *resource* servers set a cookie that keeps the user logged in after authenticated requests,
339
- // and expose the fact that they set it on a special endpoint.
340
- // After login, we store that fact in LocalStorage. This means that we can now look for that
341
- // data, and if present, indicate that the user is already logged in.
342
- // Note that there are a lot of edge cases that won't work well with this approach, so it willl
343
- // be removed in due time, and is disabled by default.
344
- if (
345
- options.useEssSession !== true ||
346
- options.restorePreviousSession === true
347
- ) {
348
- window.localStorage.setItem(
349
- "tmp-resource-server-session-enabled",
350
- "false"
351
- );
352
- } else {
353
- window.localStorage.setItem(
354
- "tmp-resource-server-session-enabled",
355
- "true"
356
- );
357
- }
358
- const storedSessionCookieReference = window.localStorage.getItem(
359
- "tmp-resource-server-session-info"
360
- );
361
- if (
362
- typeof storedSessionCookieReference === "string" &&
363
- options.restorePreviousSession !== true &&
364
- options.useEssSession === true
365
- ) {
366
- // TOOD: Re-use the type used when writing this data:
367
- // https://github.com/inrupt/solid-client-authn-js/pull/920/files#diff-659ac87dfd3711f4cfcea3c7bf6970980f4740fd59df45f04c7977bffaa23e98R118
368
- // To keep temporary code together
369
- // eslint-disable-next-line no-inner-declarations
370
- function isValidSessionCookieReference(
371
- reference: Record<string, unknown>
372
- ): reference is ResourceServerSession {
373
- const resourceServers = Object.keys(
374
- (reference as ResourceServerSession).sessions ?? {}
375
- );
376
- return (
377
- typeof (reference as ResourceServerSession).webId === "string" &&
378
- resourceServers.length > 0 &&
379
- typeof (reference as ResourceServerSession).sessions[
380
- resourceServers[0]
381
- ].expiration === "number"
382
- );
383
- }
384
- const reference = JSON.parse(storedSessionCookieReference);
385
- if (isValidSessionCookieReference(reference)) {
386
- const resourceServers = Object.keys(reference.sessions);
387
- const webIdOrigin = new URL(reference.webId).hostname;
388
- const ownResourceServer = resourceServers.find((resourceServer) => {
389
- return new URL(resourceServer).hostname === webIdOrigin;
390
- });
391
- // Usually the user's WebID is also a Resource server for them,
392
- // so we pick the expiration time for that. If it doesn't exist,
393
- // we just pick the first (and probably only) one:
394
- const relevantServer = ownResourceServer ?? resourceServers[0];
395
- // If the cookie is valid for fewer than five minutes,
396
- // pretend it's not valid anymore already, to avoid small misalignments
397
- // resulting in invalid states:
398
- if (
399
- reference.sessions[relevantServer].expiration - Date.now() >
400
- 5 * 60 * 1000
401
- ) {
402
- this.info.isLoggedIn = true;
403
- this.info.webId = reference.webId;
404
- this.tmpFetchWithCookies = true;
405
- return this.info;
406
- }
407
- }
408
- }
409
- // end of temporary workaround.
410
-
411
331
  this.tokenRequestInProgress = true;
412
332
  const sessionInfo = await this.clientAuthentication.handleIncomingRedirect(
413
333
  url,
@@ -25,12 +25,12 @@ import {
25
25
  } from "@inrupt/solid-client-authn-core";
26
26
  import { jest } from "@jest/globals";
27
27
 
28
- export const RedirectorMock: jest.Mocked<IRedirector> = {
29
- redirect: jest.fn(
30
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
- (redirectUrl: string, redirectOptions: IRedirectorOptions) => {
32
- /* void */
33
- }
34
- ),
35
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
- } as any;
28
+ export const mockedRedirect = jest.fn<
29
+ void,
30
+ [redirectUrl: string, redirectOptions: IRedirectorOptions]
31
+ >();
32
+ export const mockRedirector = (): IRedirector => {
33
+ return {
34
+ redirect: mockedRedirect,
35
+ };
36
+ };
@@ -19,23 +19,25 @@
19
19
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
  */
21
21
 
22
+ import { jest, it, describe, expect, afterEach } from "@jest/globals";
23
+ import mockConsole from "jest-mock-console";
24
+
22
25
  /**
23
26
  * Test for AuthorizationCodeWithPkceOidcHandler
24
27
  */
25
28
  import {
26
29
  IOidcOptions,
27
- IRedirector,
28
30
  IRedirectorOptions,
29
31
  mockStorage,
30
32
  StorageUtility,
31
33
  StorageUtilityMock,
32
34
  } from "@inrupt/solid-client-authn-core";
33
- import { jest, it, describe, expect } from "@jest/globals";
35
+
34
36
  import AuthorizationCodeWithPkceOidcHandler from "./AuthorizationCodeWithPkceOidcHandler";
35
37
  import canHandleTests from "./OidcHandlerCanHandleTest";
36
38
  import { SessionInfoManagerMock } from "../../../sessionInfo/__mocks__/SessionInfoManager";
37
39
  import { standardOidcOptions } from "../__mocks__/IOidcOptions";
38
- import { RedirectorMock } from "../__mocks__/Redirector";
40
+ import { mockedRedirect, mockRedirector } from "../__mocks__/Redirector";
39
41
 
40
42
  jest.mock("@inrupt/oidc-client-ext");
41
43
 
@@ -57,10 +59,14 @@ const mockOidcModule = (
57
59
  };
58
60
 
59
61
  describe("AuthorizationCodeWithPkceOidcHandler", () => {
62
+ afterEach(() => {
63
+ jest.resetAllMocks();
64
+ });
65
+
60
66
  const defaultMocks = {
61
67
  sessionCreator: SessionInfoManagerMock,
62
68
  storageUtility: StorageUtilityMock,
63
- redirector: RedirectorMock,
69
+ redirector: mockRedirector(),
64
70
  };
65
71
 
66
72
  function getAuthorizationCodeWithPkceOidcHandler(
@@ -91,20 +97,18 @@ describe("AuthorizationCodeWithPkceOidcHandler", () => {
91
97
  describe("handle", () => {
92
98
  it("swallows any redirector exceptions", async () => {
93
99
  mockOidcModule();
94
- const redirectorThatThrowsMock: jest.Mocked<IRedirector> = {
95
- redirect: jest.fn(
96
- (redirectUrl: string, redirectOptions: IRedirectorOptions) => {
97
- throw new Error(
98
- `Redirecting to [${redirectUrl}] with options [${redirectOptions}] throws...`
99
- );
100
- }
101
- ),
102
- } as any;
100
+ mockConsole("error");
101
+
102
+ mockedRedirect.mockImplementation(
103
+ (redirectUrl: string, redirectOptions: IRedirectorOptions) => {
104
+ throw new Error(
105
+ `Redirecting to [${redirectUrl}] with options [${redirectOptions}] throws...`
106
+ );
107
+ }
108
+ );
103
109
 
104
110
  const authorizationCodeWithPkceOidcHandler =
105
- getAuthorizationCodeWithPkceOidcHandler({
106
- redirector: redirectorThatThrowsMock,
107
- });
111
+ getAuthorizationCodeWithPkceOidcHandler();
108
112
 
109
113
  const oidcOptions: IOidcOptions = {
110
114
  ...standardOidcOptions,
@@ -115,8 +119,20 @@ describe("AuthorizationCodeWithPkceOidcHandler", () => {
115
119
  grantTypesSupported: ["authorization_code"],
116
120
  },
117
121
  };
118
- await authorizationCodeWithPkceOidcHandler.handle(oidcOptions);
119
- expect(redirectorThatThrowsMock.redirect).toHaveBeenCalledTimes(1);
122
+
123
+ await expect(
124
+ authorizationCodeWithPkceOidcHandler.handle(oidcOptions)
125
+ ).resolves.toBeUndefined();
126
+
127
+ expect(mockedRedirect).toHaveBeenCalledTimes(1);
128
+
129
+ // Test the error was even printed to the console Note: this matcher is
130
+ // pretty nasty due to an Error instance being logged without being
131
+ // converted to a string:
132
+ expect(console.error).toHaveBeenCalledTimes(1);
133
+ expect(
134
+ (console as jest.Mocked<Console>).error.mock.calls[0][0].toString()
135
+ ).toMatch(/Error: Redirecting to \[[^\]]+\] with options/);
120
136
  });
121
137
 
122
138
  it("handles login properly with PKCE", async () => {