@inrupt/solid-client-authn-browser 1.17.2 → 1.17.3

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 (30) hide show
  1. package/dist/ClientAuthentication.d.ts +7 -0
  2. package/dist/Session.d.ts +174 -0
  3. package/dist/defaultSession.d.ts +70 -0
  4. package/dist/dependencies.d.ts +12 -0
  5. package/dist/index.js +783 -6
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +783 -6
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/login/oidc/AggregateOidcHandler.d.ts +10 -0
  10. package/dist/login/oidc/AggregateRedirectHandler.d.ts +10 -0
  11. package/dist/login/oidc/ClientRegistrar.d.ts +7 -0
  12. package/dist/login/oidc/IssuerConfigFetcher.d.ts +10 -0
  13. package/dist/login/oidc/OidcLoginHandler.d.ts +10 -0
  14. package/dist/login/oidc/Redirector.d.ts +7 -0
  15. package/dist/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.d.ts +3 -0
  16. package/dist/login/oidc/incomingRedirectHandler/ErrorOidcHandler.d.ts +10 -0
  17. package/dist/login/oidc/incomingRedirectHandler/FallbackRedirectHandler.d.ts +10 -0
  18. package/dist/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.d.ts +12 -0
  19. package/dist/login/oidc/refresh/TokenRefresher.d.ts +7 -0
  20. package/dist/logout/buildRpInitiatedLogout.d.ts +7 -0
  21. package/dist/sessionInfo/SessionInfoManager.d.ts +18 -0
  22. package/dist/solid-client-authn.bundle.js +5 -5
  23. package/dist/solid-client-authn.bundle.js.map +1 -1
  24. package/dist/storage/BrowserStorage.d.ts +7 -0
  25. package/dist/storage/StorageUtility.d.ts +14 -0
  26. package/dist/util/UuidGenerator.d.ts +6 -0
  27. package/package.json +6 -6
  28. package/src/ClientAuthentication.spec.ts +40 -19
  29. package/src/ClientAuthentication.ts +14 -3
  30. package/src/Session.spec.ts +10 -13
@@ -1,4 +1,11 @@
1
+ /**
2
+ * @hidden
3
+ * @packageDocumentation
4
+ */
1
5
  import type { IStorage } from "@inrupt/solid-client-authn-core";
6
+ /**
7
+ * @hidden
8
+ */
2
9
  export default class BrowserStorage implements IStorage {
3
10
  get storage(): typeof window.localStorage;
4
11
  get(key: string): Promise<string | undefined>;
@@ -1,5 +1,19 @@
1
+ /**
2
+ * @hidden
3
+ * @packageDocumentation
4
+ */
5
+ /**
6
+ * A helper class that will validate items taken from local storage
7
+ */
1
8
  import type { IStorage } from "@inrupt/solid-client-authn-core";
2
9
  import { StorageUtility } from "@inrupt/solid-client-authn-core";
10
+ /**
11
+ * This class in a no-value-added extension of StorageUtility from the core module.
12
+ * The reason it has to be declared is for TSyringe to find the decorators in the
13
+ * same modules as where the dependency container is declared (in this case,
14
+ * the browser module, with the dependancy container in dependencies.ts).
15
+ * @hidden
16
+ */
3
17
  export default class StorageUtilityBrowser extends StorageUtility {
4
18
  constructor(secureStorage: IStorage, insecureStorage: IStorage);
5
19
  }
@@ -1,6 +1,12 @@
1
+ /**
2
+ * @hidden
3
+ */
1
4
  export interface IUuidGenerator {
2
5
  v4(): string;
3
6
  }
7
+ /**
8
+ * @hidden
9
+ */
4
10
  export default class UuidGenerator {
5
11
  v4(): string;
6
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inrupt/solid-client-authn-browser",
3
- "version": "1.17.2",
3
+ "version": "1.17.3",
4
4
  "license": "MIT",
5
5
  "types": "dist/index.d.ts",
6
6
  "browser": "dist/index.js",
@@ -26,18 +26,18 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^20.6.0",
29
- "@types/uuid": "^9.0.1"
29
+ "@types/uuid": "^9.0.4"
30
30
  },
31
31
  "dependencies": {
32
- "@inrupt/oidc-client-ext": "^1.17.2",
33
- "@inrupt/solid-client-authn-core": "^1.17.2",
32
+ "@inrupt/oidc-client-ext": "^1.17.3",
33
+ "@inrupt/solid-client-authn-core": "^1.17.3",
34
34
  "@inrupt/universal-fetch": "^1.0.2",
35
35
  "events": "^3.3.0",
36
36
  "jose": "^4.14.6",
37
- "uuid": "^9.0.0"
37
+ "uuid": "^9.0.1"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "5818a79b875812ca93235fca64c65d2ba5b59fc3"
42
+ "gitHead": "ef10e8366ee8511f726b6cf5918f9e9c6653c6cc"
43
43
  }
@@ -336,8 +336,12 @@ describe("ClientAuthentication", () => {
336
336
  // TODO: add tests for events & errors
337
337
 
338
338
  it("reverts back to un-authenticated fetch on logout", async () => {
339
- // eslint-disable-next-line no-restricted-globals
340
- history.replaceState = jest.fn();
339
+ window.history.replaceState = jest
340
+ .fn<typeof window.history.replaceState>()
341
+ .mockImplementationOnce((_data, _unused, url) => {
342
+ // Pretend the current location is updated
343
+ window.location.href = url as string;
344
+ });
341
345
  const clientAuthn = getClientAuthentication();
342
346
 
343
347
  const unauthFetch = clientAuthn.fetch;
@@ -402,13 +406,17 @@ describe("ClientAuthentication", () => {
402
406
  mockEmitter.emit = jest.fn<typeof mockEmitter.emit>();
403
407
 
404
408
  it("calls handle redirect", async () => {
405
- // eslint-disable-next-line no-restricted-globals
406
- history.replaceState = jest.fn();
409
+ window.history.replaceState = jest
410
+ .fn<typeof window.history.replaceState>()
411
+ .mockImplementationOnce((_data, _unused, url) => {
412
+ // Pretend the current location is updated
413
+ window.location.href = url as string;
414
+ });
407
415
  const expectedResult = SessionCreatorCreateResponse;
408
416
  const clientAuthn = getClientAuthentication();
409
417
  const unauthFetch = clientAuthn.fetch;
410
418
  const url =
411
- "https://coolapp.com/redirect?state=userId&id_token=idToken&access_token=accessToken";
419
+ "https://example.org/redirect?state=userId&id_token=idToken&access_token=accessToken";
412
420
  const redirectInfo = await clientAuthn.handleIncomingRedirect(
413
421
  url,
414
422
  mockEmitter,
@@ -436,8 +444,12 @@ describe("ClientAuthentication", () => {
436
444
  });
437
445
 
438
446
  it("clears the current IRI from OAuth query parameters in the auth code flow", async () => {
439
- // eslint-disable-next-line no-restricted-globals
440
- history.replaceState = jest.fn();
447
+ window.history.replaceState = jest
448
+ .fn<typeof window.history.replaceState>()
449
+ .mockImplementationOnce((_data, _unused, url) => {
450
+ // Pretend the current location is updated
451
+ window.location.href = url as string;
452
+ });
441
453
  const clientAuthn = getClientAuthentication();
442
454
  const url =
443
455
  "https://coolapp.com/redirect?state=someState&code=someAuthCode&iss=someIssuer";
@@ -452,8 +464,12 @@ describe("ClientAuthentication", () => {
452
464
  });
453
465
 
454
466
  it("clears the current IRI from OAuth query parameters even if auth flow fails", async () => {
455
- // eslint-disable-next-line no-restricted-globals
456
- history.replaceState = jest.fn();
467
+ window.history.replaceState = jest
468
+ .fn<typeof window.history.replaceState>()
469
+ .mockImplementationOnce((_data, _unused, url) => {
470
+ // Pretend the current location is updated
471
+ window.location.href = url as string;
472
+ });
457
473
 
458
474
  mockHandleIncomingRedirect.mockImplementationOnce(() =>
459
475
  Promise.reject(new Error("Something went wrong")),
@@ -464,8 +480,7 @@ describe("ClientAuthentication", () => {
464
480
  const url =
465
481
  "https://coolapp.com/redirect?state=someState&code=someAuthCode";
466
482
  await clientAuthn.handleIncomingRedirect(url, mockEmitter);
467
- // eslint-disable-next-line no-restricted-globals
468
- expect(history.replaceState).toHaveBeenCalledWith(
483
+ expect(window.history.replaceState).toHaveBeenCalledWith(
469
484
  null,
470
485
  "",
471
486
  "https://coolapp.com/redirect",
@@ -479,14 +494,17 @@ describe("ClientAuthentication", () => {
479
494
  });
480
495
 
481
496
  it("clears the current IRI from OAuth query parameters in the implicit flow", async () => {
482
- // eslint-disable-next-line no-restricted-globals
483
- history.replaceState = jest.fn();
497
+ window.history.replaceState = jest
498
+ .fn<typeof window.history.replaceState>()
499
+ .mockImplementationOnce((_data, _unused, url) => {
500
+ // Pretend the current location is updated
501
+ window.location.href = url as string;
502
+ });
484
503
  const clientAuthn = getClientAuthentication();
485
504
  const url =
486
505
  "https://coolapp.com/redirect?state=someState&id_token=idToken&access_token=accessToken";
487
506
  await clientAuthn.handleIncomingRedirect(url, mockEmitter);
488
- // eslint-disable-next-line no-restricted-globals
489
- expect(history.replaceState).toHaveBeenCalledWith(
507
+ expect(window.history.replaceState).toHaveBeenCalledWith(
490
508
  null,
491
509
  "",
492
510
  "https://coolapp.com/redirect",
@@ -495,14 +513,17 @@ describe("ClientAuthentication", () => {
495
513
  });
496
514
 
497
515
  it("preserves non-OAuth query strings", async () => {
498
- // eslint-disable-next-line no-restricted-globals
499
- history.replaceState = jest.fn();
516
+ window.history.replaceState = jest
517
+ .fn<typeof window.history.replaceState>()
518
+ .mockImplementationOnce((_data, _unused, url) => {
519
+ // Pretend the current location is updated
520
+ window.location.href = url as string;
521
+ });
500
522
  const clientAuthn = getClientAuthentication();
501
523
  const url =
502
524
  "https://coolapp.com/redirect?state=someState&code=someAuthCode&someQuery=someValue";
503
525
  await clientAuthn.handleIncomingRedirect(url, mockEmitter);
504
- // eslint-disable-next-line no-restricted-globals
505
- expect(history.replaceState).toHaveBeenCalledWith(
526
+ expect(window.history.replaceState).toHaveBeenCalledWith(
506
527
  null,
507
528
  "",
508
529
  "https://coolapp.com/redirect?someQuery=someValue",
@@ -109,7 +109,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
109
109
  this.boundLogout = redirectInfo.getLogoutUrl;
110
110
 
111
111
  // Strip the oauth params:
112
- this.cleanUrlAfterRedirect(url);
112
+ await this.cleanUrlAfterRedirect(url);
113
113
 
114
114
  return {
115
115
  isLoggedIn: redirectInfo.isLoggedIn,
@@ -119,7 +119,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
119
119
  };
120
120
  } catch (err) {
121
121
  // Strip the oauth params:
122
- this.cleanUrlAfterRedirect(url);
122
+ await this.cleanUrlAfterRedirect(url);
123
123
 
124
124
  // FIXME: EVENTS.ERROR should be errorCode, errorDescription
125
125
  //
@@ -132,7 +132,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
132
132
  }
133
133
  };
134
134
 
135
- private cleanUrlAfterRedirect(url: string): void {
135
+ private async cleanUrlAfterRedirect(url: string): Promise<void> {
136
136
  const cleanedUpUrl = new URL(url);
137
137
  cleanedUpUrl.searchParams.delete("state");
138
138
  // For auth code flow
@@ -151,5 +151,16 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
151
151
  // authentication library will be called again with what are now invalid
152
152
  // query parameters!).
153
153
  window.history.replaceState(null, "", cleanedUpUrl.toString());
154
+ while (window.location.href !== cleanedUpUrl.href) {
155
+ // Poll the current URL every ms. Active polling is required because
156
+ // window.history.replaceState is asynchronous, but the associated
157
+ // 'popstate' event which should be listened to is only sent on active
158
+ // navigation, which we will not have here.
159
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
160
+ // eslint-disable-next-line no-await-in-loop
161
+ await new Promise<void>((resolve) => {
162
+ setTimeout(() => resolve(), 1);
163
+ });
164
+ }
154
165
  }
155
166
  }
@@ -246,10 +246,9 @@ describe("Session", () => {
246
246
  it("uses current window location as default redirect URL", async () => {
247
247
  mockLocation("https://some.url");
248
248
  const clientAuthentication = mockClientAuthentication();
249
- const incomingRedirectHandler = jest.spyOn(
250
- clientAuthentication,
251
- "handleIncomingRedirect",
252
- );
249
+ const incomingRedirectHandler = jest
250
+ .spyOn(clientAuthentication, "handleIncomingRedirect")
251
+ .mockResolvedValue(undefined);
253
252
 
254
253
  const mySession = new Session({ clientAuthentication });
255
254
  await mySession.handleIncomingRedirect();
@@ -259,13 +258,12 @@ describe("Session", () => {
259
258
  );
260
259
  });
261
260
 
262
- it("wraps up ClientAuthentication handleIncomingRedirect", async () => {
261
+ it("wraps ClientAuthentication handleIncomingRedirect", async () => {
263
262
  mockLocation("https://some.url");
264
263
  const clientAuthentication = mockClientAuthentication();
265
- const incomingRedirectHandler = jest.spyOn(
266
- clientAuthentication,
267
- "handleIncomingRedirect",
268
- );
264
+ const incomingRedirectHandler = jest
265
+ .spyOn(clientAuthentication, "handleIncomingRedirect")
266
+ .mockResolvedValue(undefined);
269
267
  const mySession = new Session({ clientAuthentication });
270
268
  await mySession.handleIncomingRedirect("https://some.url");
271
269
  expect(incomingRedirectHandler).toHaveBeenCalled();
@@ -401,10 +399,9 @@ describe("Session", () => {
401
399
 
402
400
  it("preserves a binding to its Session instance", async () => {
403
401
  const clientAuthentication = mockClientAuthentication();
404
- const incomingRedirectHandler = jest.spyOn(
405
- clientAuthentication,
406
- "handleIncomingRedirect",
407
- );
402
+ const incomingRedirectHandler = jest
403
+ .spyOn(clientAuthentication, "handleIncomingRedirect")
404
+ .mockResolvedValue(undefined);
408
405
  const mySession = new Session({ clientAuthentication });
409
406
  const objectWithHandleIncomingRedirect = {
410
407
  handleIncomingRedirect: mySession.handleIncomingRedirect,