@inrupt/solid-client-authn-browser 1.17.1 → 1.17.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 (38) hide show
  1. package/dist/constant.d.ts +2 -2
  2. package/dist/index.js +50 -28
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +50 -28
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/solid-client-authn.bundle.js +7 -7
  7. package/dist/solid-client-authn.bundle.js.map +1 -1
  8. package/package.json +6 -6
  9. package/rollup.config.mjs +1 -1
  10. package/src/ClientAuthentication.spec.ts +66 -38
  11. package/src/ClientAuthentication.ts +4 -4
  12. package/src/Session.spec.ts +44 -44
  13. package/src/Session.ts +24 -18
  14. package/src/__mocks__/ClientAuthentication.ts +2 -2
  15. package/src/defaultSession.ts +4 -3
  16. package/src/dependencies.ts +5 -5
  17. package/src/login/oidc/ClientRegistrar.spec.ts +22 -22
  18. package/src/login/oidc/ClientRegistrar.ts +4 -2
  19. package/src/login/oidc/IssuerConfigFetcher.spec.ts +25 -25
  20. package/src/login/oidc/IssuerConfigFetcher.ts +8 -6
  21. package/src/login/oidc/OidcLoginHandler.spec.ts +15 -15
  22. package/src/login/oidc/OidcLoginHandler.ts +14 -9
  23. package/src/login/oidc/Redirector.spec.ts +1 -1
  24. package/src/login/oidc/__mocks__/ClientRegistrar.ts +3 -3
  25. package/src/login/oidc/__mocks__/IssuerConfigFetcher.ts +3 -3
  26. package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.spec.ts +42 -42
  27. package/src/login/oidc/incomingRedirectHandler/AuthCodeRedirectHandler.ts +19 -13
  28. package/src/login/oidc/incomingRedirectHandler/ErrorOidcHandler.spec.ts +12 -12
  29. package/src/login/oidc/incomingRedirectHandler/ErrorOidcHandler.ts +2 -2
  30. package/src/login/oidc/incomingRedirectHandler/FallbackRedirectHandler.spec.ts +7 -7
  31. package/src/login/oidc/incomingRedirectHandler/FallbackRedirectHandler.ts +2 -2
  32. package/src/login/oidc/oidcHandlers/AuthorizationCodeWithPkceOidcHandler.spec.ts +17 -17
  33. package/src/login/oidc/refresh/TokenRefresher.spec.ts +14 -14
  34. package/src/login/oidc/refresh/TokenRefresher.ts +12 -8
  35. package/src/login/oidc/refresh/__mocks__/TokenRefresher.ts +1 -1
  36. package/src/sessionInfo/SessionInfoManager.spec.ts +37 -15
  37. package/src/sessionInfo/SessionInfoManager.ts +46 -50
  38. package/src/sessionInfo/__mocks__/SessionInfoManager.ts +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inrupt/solid-client-authn-browser",
3
- "version": "1.17.1",
3
+ "version": "1.17.2",
4
4
  "license": "MIT",
5
5
  "types": "dist/index.d.ts",
6
6
  "browser": "dist/index.js",
@@ -25,19 +25,19 @@
25
25
  "build-docs-preview-site": "npm run build-api-docs; cd docs/api; make html"
26
26
  },
27
27
  "devDependencies": {
28
- "@types/node": "^20.1.0",
28
+ "@types/node": "^20.6.0",
29
29
  "@types/uuid": "^9.0.1"
30
30
  },
31
31
  "dependencies": {
32
- "@inrupt/oidc-client-ext": "^1.17.1",
33
- "@inrupt/solid-client-authn-core": "^1.17.1",
32
+ "@inrupt/oidc-client-ext": "^1.17.2",
33
+ "@inrupt/solid-client-authn-core": "^1.17.2",
34
34
  "@inrupt/universal-fetch": "^1.0.2",
35
35
  "events": "^3.3.0",
36
- "jose": "^4.3.7",
36
+ "jose": "^4.14.6",
37
37
  "uuid": "^9.0.0"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "0c27ce0ae0da67838526a538adc78b2f96f9b96b"
42
+ "gitHead": "5818a79b875812ca93235fca64c65d2ba5b59fc3"
43
43
  }
package/rollup.config.mjs CHANGED
@@ -7,7 +7,7 @@ import { createRequire } from "node:module";
7
7
  import { nodeResolve } from '@rollup/plugin-node-resolve';
8
8
  import commonjs from '@rollup/plugin-commonjs';
9
9
  import terser from '@rollup/plugin-terser';
10
- import createConfig, { createSharedConfig } from '../../rollup.common.mjs';
10
+ import createConfig, { createSharedConfig } from '@inrupt/base-rollup-config';
11
11
 
12
12
  const require = createRequire(import.meta.url);
13
13
  const pkg = require("./package.json");
@@ -50,7 +50,7 @@ import { mockDefaultIssuerConfigFetcher } from "./login/oidc/__mocks__/IssuerCon
50
50
 
51
51
  jest.mock("@inrupt/universal-fetch", () => {
52
52
  const fetchModule = jest.requireActual(
53
- "@inrupt/universal-fetch"
53
+ "@inrupt/universal-fetch",
54
54
  ) as typeof UniversalFetch;
55
55
  return {
56
56
  ...fetchModule,
@@ -60,7 +60,7 @@ jest.mock("@inrupt/universal-fetch", () => {
60
60
 
61
61
  jest.mock("@inrupt/solid-client-authn-core", () => {
62
62
  const actualCoreModule = jest.requireActual(
63
- "@inrupt/solid-client-authn-core"
63
+ "@inrupt/solid-client-authn-core",
64
64
  ) as typeof SolidClientAuthnCore;
65
65
  return {
66
66
  ...actualCoreModule,
@@ -78,7 +78,7 @@ const mockSessionStorage = async (
78
78
  options: SessionStorageOptions = {
79
79
  clientId: "https://some.app/registration",
80
80
  issuer: "https://some.issuer",
81
- }
81
+ },
82
82
  ): Promise<StorageUtility> => {
83
83
  return new StorageUtility(
84
84
  mockStorage({
@@ -92,7 +92,7 @@ const mockSessionStorage = async (
92
92
  clientId: options.clientId,
93
93
  issuer: options.issuer,
94
94
  },
95
- })
95
+ }),
96
96
  );
97
97
  };
98
98
 
@@ -107,14 +107,14 @@ describe("ClientAuthentication", () => {
107
107
  };
108
108
 
109
109
  function getClientAuthentication(
110
- mocks: Partial<typeof defaultMocks> = defaultMocks
110
+ mocks: Partial<typeof defaultMocks> = defaultMocks,
111
111
  ): ClientAuthentication {
112
112
  return new ClientAuthentication(
113
113
  mocks.loginHandler ?? defaultMocks.loginHandler,
114
114
  mocks.redirectHandler ?? defaultMocks.redirectHandler,
115
115
  mocks.logoutHandler ?? defaultMocks.logoutHandler,
116
116
  mocks.sessionInfoManager ?? defaultMocks.sessionInfoManager,
117
- mocks.issuerConfigFetcher ?? defaultMocks.issuerConfigFetcher
117
+ mocks.issuerConfigFetcher ?? defaultMocks.issuerConfigFetcher,
118
118
  );
119
119
  }
120
120
 
@@ -144,7 +144,7 @@ describe("ClientAuthentication", () => {
144
144
  clientId: "coolApp",
145
145
  oidcIssuer: "https://idp.com",
146
146
  },
147
- mockEmitter
147
+ mockEmitter,
148
148
  );
149
149
  expect(defaultMocks.loginHandler.handle).toHaveBeenCalledWith({
150
150
  sessionId: "mySession",
@@ -167,7 +167,7 @@ describe("ClientAuthentication", () => {
167
167
  redirectUrl: "https://coolapp.com/redirect",
168
168
  oidcIssuer: "https://idp.com",
169
169
  },
170
- mockEmitter
170
+ mockEmitter,
171
171
  );
172
172
  expect(defaultMocks.loginHandler.handle).toHaveBeenCalledWith({
173
173
  sessionId: "mySession",
@@ -192,7 +192,7 @@ describe("ClientAuthentication", () => {
192
192
  oidcIssuer: "https://idp.com",
193
193
  tokenType: "Bearer",
194
194
  },
195
- mockEmitter
195
+ mockEmitter,
196
196
  );
197
197
  expect(defaultMocks.loginHandler.handle).toHaveBeenCalledWith({
198
198
  sessionId: "mySession",
@@ -214,7 +214,7 @@ describe("ClientAuthentication", () => {
214
214
  await nonEmptyStorage.setForUser(
215
215
  "someUser",
216
216
  { someKey: "someValue" },
217
- { secure: true }
217
+ { secure: true },
218
218
  );
219
219
  const clientAuthn = getClientAuthentication({
220
220
  sessionInfoManager: mockSessionInfoManager(nonEmptyStorage),
@@ -228,17 +228,17 @@ describe("ClientAuthentication", () => {
228
228
  redirectUrl: "https://coolapp.com/redirect",
229
229
  oidcIssuer: "https://idp.com",
230
230
  },
231
- mockEmitter
231
+ mockEmitter,
232
232
  );
233
233
  await expect(
234
- nonEmptyStorage.getForUser("someUser", "someKey", { secure: true })
234
+ nonEmptyStorage.getForUser("someUser", "someKey", { secure: true }),
235
235
  ).resolves.toBeUndefined();
236
236
  await expect(
237
- nonEmptyStorage.getForUser("someUser", "someKey", { secure: false })
237
+ nonEmptyStorage.getForUser("someUser", "someKey", { secure: false }),
238
238
  ).resolves.toBeUndefined();
239
239
  // This test is only necessary until the key is stored safely
240
240
  await expect(
241
- nonEmptyStorage.get("clientKey", { secure: false })
241
+ nonEmptyStorage.get("clientKey", { secure: false }),
242
242
  ).resolves.toBeUndefined();
243
243
  });
244
244
 
@@ -253,8 +253,8 @@ describe("ClientAuthentication", () => {
253
253
  redirectUrl: "not a valid URL",
254
254
  oidcIssuer: "https://idp.com",
255
255
  },
256
- mockEmitter
257
- )
256
+ mockEmitter,
257
+ ),
258
258
  ).rejects.toThrow();
259
259
  });
260
260
 
@@ -269,11 +269,39 @@ describe("ClientAuthentication", () => {
269
269
  redirectUrl: "https://example.org/redirect#some-fragment",
270
270
  oidcIssuer: "https://idp.com",
271
271
  },
272
- mockEmitter
273
- )
272
+ mockEmitter,
273
+ ),
274
274
  ).rejects.toThrow("hash fragment");
275
275
  });
276
276
 
277
+ it("throws if the redirect IRI contains a reserved query parameter, with a helpful message", async () => {
278
+ const clientAuthn = getClientAuthentication();
279
+ await expect(() =>
280
+ clientAuthn.login(
281
+ {
282
+ sessionId: "someUser",
283
+ tokenType: "DPoP",
284
+ clientId: "coolApp",
285
+ redirectUrl: "https://example.org/redirect?state=1234",
286
+ oidcIssuer: "https://idp.com",
287
+ },
288
+ mockEmitter,
289
+ ),
290
+ ).rejects.toThrow("query parameter");
291
+ await expect(() =>
292
+ clientAuthn.login(
293
+ {
294
+ sessionId: "someUser",
295
+ tokenType: "DPoP",
296
+ clientId: "coolApp",
297
+ redirectUrl: "https://example.org/redirect?code=1234",
298
+ oidcIssuer: "https://idp.com",
299
+ },
300
+ mockEmitter,
301
+ ),
302
+ ).rejects.toThrow("query parameter");
303
+ });
304
+
277
305
  it("does not normalize the redirect URL if provided by the user", async () => {
278
306
  const clientAuthn = getClientAuthentication();
279
307
  await clientAuthn.login(
@@ -285,12 +313,12 @@ describe("ClientAuthentication", () => {
285
313
  oidcIssuer: "https://idp.com",
286
314
  tokenType: "Bearer",
287
315
  },
288
- mockEmitter
316
+ mockEmitter,
289
317
  );
290
318
  expect(defaultMocks.loginHandler.handle).toHaveBeenCalledWith(
291
319
  expect.objectContaining({
292
320
  redirectUrl: "https://example.org",
293
- })
321
+ }),
294
322
  );
295
323
  });
296
324
  });
@@ -337,7 +365,7 @@ describe("ClientAuthentication", () => {
337
365
  it("creates a session for the global user", async () => {
338
366
  const clientAuthn = getClientAuthentication();
339
367
  await expect(() => clientAuthn.getAllSessionInfo()).rejects.toThrow(
340
- "Not implemented"
368
+ "Not implemented",
341
369
  );
342
370
  });
343
371
  });
@@ -355,8 +383,8 @@ describe("ClientAuthentication", () => {
355
383
  {
356
384
  "solidClientAuthenticationUser:mySession": { ...sessionInfo },
357
385
  },
358
- true
359
- )
386
+ true,
387
+ ),
360
388
  ),
361
389
  });
362
390
  const session = await clientAuthn.getSessionInfo("mySession");
@@ -383,7 +411,7 @@ describe("ClientAuthentication", () => {
383
411
  "https://coolapp.com/redirect?state=userId&id_token=idToken&access_token=accessToken";
384
412
  const redirectInfo = await clientAuthn.handleIncomingRedirect(
385
413
  url,
386
- mockEmitter
414
+ mockEmitter,
387
415
  );
388
416
 
389
417
  // Our injected mocked response may also contain internal-only data (for
@@ -395,11 +423,11 @@ describe("ClientAuthentication", () => {
395
423
  expect(redirectInfo?.webId).toEqual(expectedResult.webId);
396
424
  expect(redirectInfo?.isLoggedIn).toEqual(expectedResult.isLoggedIn);
397
425
  expect(redirectInfo?.expirationDate).toEqual(
398
- expectedResult.expirationDate
426
+ expectedResult.expirationDate,
399
427
  );
400
428
  expect(defaultMocks.redirectHandler.handle).toHaveBeenCalledWith(
401
429
  url,
402
- mockEmitter
430
+ mockEmitter,
403
431
  );
404
432
 
405
433
  // Calling the redirect handler should have updated the fetch.
@@ -418,7 +446,7 @@ describe("ClientAuthentication", () => {
418
446
  expect(history.replaceState).toHaveBeenCalledWith(
419
447
  null,
420
448
  "",
421
- "https://coolapp.com/redirect"
449
+ "https://coolapp.com/redirect",
422
450
  );
423
451
  expect(mockEmitter.emit).not.toHaveBeenCalled();
424
452
  });
@@ -428,7 +456,7 @@ describe("ClientAuthentication", () => {
428
456
  history.replaceState = jest.fn();
429
457
 
430
458
  mockHandleIncomingRedirect.mockImplementationOnce(() =>
431
- Promise.reject(new Error("Something went wrong"))
459
+ Promise.reject(new Error("Something went wrong")),
432
460
  );
433
461
 
434
462
  const clientAuthn = getClientAuthentication();
@@ -440,13 +468,13 @@ describe("ClientAuthentication", () => {
440
468
  expect(history.replaceState).toHaveBeenCalledWith(
441
469
  null,
442
470
  "",
443
- "https://coolapp.com/redirect"
471
+ "https://coolapp.com/redirect",
444
472
  );
445
473
 
446
474
  expect(mockEmitter.emit).toHaveBeenCalledWith(
447
475
  EVENTS.ERROR,
448
476
  "redirect",
449
- new Error("Something went wrong")
477
+ new Error("Something went wrong"),
450
478
  );
451
479
  });
452
480
 
@@ -461,7 +489,7 @@ describe("ClientAuthentication", () => {
461
489
  expect(history.replaceState).toHaveBeenCalledWith(
462
490
  null,
463
491
  "",
464
- "https://coolapp.com/redirect"
492
+ "https://coolapp.com/redirect",
465
493
  );
466
494
  expect(mockEmitter.emit).not.toHaveBeenCalled();
467
495
  });
@@ -477,7 +505,7 @@ describe("ClientAuthentication", () => {
477
505
  expect(history.replaceState).toHaveBeenCalledWith(
478
506
  null,
479
507
  "",
480
- "https://coolapp.com/redirect?someQuery=someValue"
508
+ "https://coolapp.com/redirect?someQuery=someValue",
481
509
  );
482
510
  expect(mockEmitter.emit).not.toHaveBeenCalled();
483
511
  });
@@ -497,14 +525,14 @@ describe("ClientAuthentication", () => {
497
525
  [`${USER_SESSION_PREFIX}:${sessionId}`]: {
498
526
  clientId: "https://some.app/registration",
499
527
  },
500
- })
528
+ }),
501
529
  );
502
530
  const clientAuthn = getClientAuthentication({
503
531
  sessionInfoManager: mockSessionInfoManager(mockedStorage),
504
532
  });
505
533
 
506
534
  await expect(
507
- clientAuthn.validateCurrentSession(sessionId)
535
+ clientAuthn.validateCurrentSession(sessionId),
508
536
  ).resolves.toBeNull();
509
537
  });
510
538
 
@@ -519,14 +547,14 @@ describe("ClientAuthentication", () => {
519
547
  }),
520
548
  mockStorage({
521
549
  [`${USER_SESSION_PREFIX}:${sessionId}`]: {},
522
- })
550
+ }),
523
551
  );
524
552
  const clientAuthn = getClientAuthentication({
525
553
  sessionInfoManager: mockSessionInfoManager(mockedStorage),
526
554
  });
527
555
 
528
556
  await expect(
529
- clientAuthn.validateCurrentSession(sessionId)
557
+ clientAuthn.validateCurrentSession(sessionId),
530
558
  ).resolves.toBeNull();
531
559
  });
532
560
 
@@ -542,14 +570,14 @@ describe("ClientAuthentication", () => {
542
570
  });
543
571
 
544
572
  await expect(
545
- clientAuthn.validateCurrentSession(sessionId)
573
+ clientAuthn.validateCurrentSession(sessionId),
546
574
  ).resolves.toStrictEqual(
547
575
  expect.objectContaining({
548
576
  issuer: "https://some.issuer",
549
577
  clientAppId: "https://some.app/registration",
550
578
  sessionId,
551
579
  webId: "https://my.pod/profile#me",
552
- })
580
+ }),
553
581
  );
554
582
  });
555
583
  });
@@ -45,7 +45,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
45
45
  // Isn't Javascript fun?
46
46
  login = async (
47
47
  options: ILoginOptions,
48
- eventEmitter: EventEmitter
48
+ eventEmitter: EventEmitter,
49
49
  ): Promise<void> => {
50
50
  // In order to get a clean start, make sure that the session is logged out
51
51
  // on login.
@@ -65,7 +65,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
65
65
  options.redirectUrl ?? removeOidcQueryParam(window.location.href);
66
66
  if (!isValidRedirectUrl(redirectUrl)) {
67
67
  throw new Error(
68
- `${redirectUrl} is not a valid redirect URL, it is either a malformed IRI or it includes a hash fragment.`
68
+ `${redirectUrl} is not a valid redirect URL, it is either a malformed IRI, includes a hash fragment, or reserved query parameters ('code' or 'state').`,
69
69
  );
70
70
  }
71
71
  await this.loginHandler.handle({
@@ -82,7 +82,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
82
82
  // Note that the ID token is not stored, which means the session information
83
83
  // cannot be validated at this point.
84
84
  validateCurrentSession = async (
85
- currentSessionId: string
85
+ currentSessionId: string,
86
86
  ): Promise<(ISessionInfo & ISessionInternalInfo) | null> => {
87
87
  const sessionInfo = await this.sessionInfoManager.get(currentSessionId);
88
88
  if (
@@ -97,7 +97,7 @@ export default class ClientAuthentication extends ClientAuthenticationBase {
97
97
 
98
98
  handleIncomingRedirect = async (
99
99
  url: string,
100
- eventEmitter: EventEmitter
100
+ eventEmitter: EventEmitter,
101
101
  ): Promise<ISessionInfo | undefined> => {
102
102
  try {
103
103
  const redirectInfo = await this.redirectHandler.handle(url, eventEmitter);