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

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 -16
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +785 -18
  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 +7 -7
  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 +7 -7
  28. package/src/ClientAuthentication.spec.ts +33 -31
  29. package/src/ClientAuthentication.ts +19 -17
  30. package/src/Session.spec.ts +10 -13
package/dist/index.mjs CHANGED
@@ -1,33 +1,102 @@
1
- import { StorageUtility, ClientAuthentication as ClientAuthentication$1, isValidRedirectUrl, EVENTS, ConfigurationError, handleRegistration, AuthorizationCodeWithPkceOidcHandlerBase, DEFAULT_SCOPES, SessionInfoManagerBase, isSupportedTokenType, clear as clear$1, getUnauthenticatedSession, loadOidcContextFromStorage, buildAuthenticatedFetch, maybeBuildRpInitiatedLogout, AggregateHandler, InMemoryStorage, IWaterfallLogoutHandler, SOLID_CLIENT_AUTHN_KEY_PREFIX, buildProxyHandler } from '@inrupt/solid-client-authn-core';
1
+ import { StorageUtility, ClientAuthentication as ClientAuthentication$1, isValidRedirectUrl, EVENTS, removeOpenIdParams, ConfigurationError, handleRegistration, AuthorizationCodeWithPkceOidcHandlerBase, DEFAULT_SCOPES, SessionInfoManagerBase, isSupportedTokenType, clear as clear$1, getUnauthenticatedSession, loadOidcContextFromStorage, buildAuthenticatedFetch, maybeBuildRpInitiatedLogout, AggregateHandler, InMemoryStorage, IWaterfallLogoutHandler, SOLID_CLIENT_AUTHN_KEY_PREFIX, buildProxyHandler } from '@inrupt/solid-client-authn-core';
2
2
  export { ConfigurationError, EVENTS, InMemoryStorage, NotImplementedError } from '@inrupt/solid-client-authn-core';
3
3
  import { v4 } from 'uuid';
4
4
  import EventEmitter from 'events';
5
- import { removeOidcQueryParam, OidcClient, clearOidcPersistentStorage, getDpopToken, getBearerToken, registerClient, refresh } from '@inrupt/oidc-client-ext';
5
+ import { normalizeCallbackUrl, OidcClient, clearOidcPersistentStorage, getDpopToken, getBearerToken, registerClient, refresh } from '@inrupt/oidc-client-ext';
6
6
  import { fetch as fetch$1 } from '@inrupt/universal-fetch';
7
7
 
8
+ //
9
+ // Copyright Inrupt Inc.
10
+ //
11
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ // of this software and associated documentation files (the "Software"), to deal in
13
+ // the Software without restriction, including without limitation the rights to use,
14
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
15
+ // Software, and to permit persons to whom the Software is furnished to do so,
16
+ // subject to the following conditions:
17
+ //
18
+ // The above copyright notice and this permission notice shall be included in
19
+ // all copies or substantial portions of the Software.
20
+ //
21
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
22
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ //
28
+ /**
29
+ * This class in a no-value-added extension of StorageUtility from the core module.
30
+ * The reason it has to be declared is for TSyringe to find the decorators in the
31
+ * same modules as where the dependency container is declared (in this case,
32
+ * the browser module, with the dependancy container in dependencies.ts).
33
+ * @hidden
34
+ */
8
35
  class StorageUtilityBrowser extends StorageUtility {
9
36
  constructor(secureStorage, insecureStorage) {
10
37
  super(secureStorage, insecureStorage);
11
38
  }
12
39
  }
13
40
 
41
+ //
42
+ // Copyright Inrupt Inc.
43
+ //
44
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
45
+ // of this software and associated documentation files (the "Software"), to deal in
46
+ // the Software without restriction, including without limitation the rights to use,
47
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
48
+ // Software, and to permit persons to whom the Software is furnished to do so,
49
+ // subject to the following conditions:
50
+ //
51
+ // The above copyright notice and this permission notice shall be included in
52
+ // all copies or substantial portions of the Software.
53
+ //
54
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
55
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
56
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
57
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
58
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
59
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
60
+ //
61
+ /**
62
+ * @hidden
63
+ */
14
64
  class ClientAuthentication extends ClientAuthentication$1 {
15
65
  constructor() {
16
66
  super(...arguments);
67
+ // Define these functions as properties so that they don't get accidentally re-bound.
68
+ // Isn't Javascript fun?
17
69
  this.login = async (options, eventEmitter) => {
18
70
  var _a, _b;
71
+ // In order to get a clean start, make sure that the session is logged out
72
+ // on login.
73
+ // But we may want to preserve our client application info, particularly if
74
+ // we used Dynamic Client Registration to register (since we don't
75
+ // necessarily want the user to have to register this app each time they
76
+ // login).
19
77
  await this.sessionInfoManager.clear(options.sessionId);
20
- const redirectUrl = (_a = options.redirectUrl) !== null && _a !== void 0 ? _a : removeOidcQueryParam(window.location.href);
78
+ // In the case of the user hitting the 'back' button in their browser, they
79
+ // could return to a previous redirect URL that contains OIDC params that
80
+ // are now longer valid. To be safe, strip relevant params now.
81
+ // If the user is providing a redirect IRI, it should not be modified, so
82
+ // normalization only applies if we default to the current location (which is
83
+ // a bad practice and should be discouraged).
84
+ const redirectUrl = (_a = options.redirectUrl) !== null && _a !== void 0 ? _a : normalizeCallbackUrl(window.location.href);
21
85
  if (!isValidRedirectUrl(redirectUrl)) {
22
86
  throw new Error(`${redirectUrl} is not a valid redirect URL, it is either a malformed IRI, includes a hash fragment, or reserved query parameters ('code' or 'state').`);
23
87
  }
24
88
  await this.loginHandler.handle({
25
89
  ...options,
26
90
  redirectUrl,
91
+ // If no clientName is provided, the clientId may be used instead.
27
92
  clientName: (_b = options.clientName) !== null && _b !== void 0 ? _b : options.clientId,
28
93
  eventEmitter,
29
94
  });
30
95
  };
96
+ // Collects session information from storage, and returns them. Returns null
97
+ // if the expected information cannot be found.
98
+ // Note that the ID token is not stored, which means the session information
99
+ // cannot be validated at this point.
31
100
  this.validateCurrentSession = async (currentSessionId) => {
32
101
  const sessionInfo = await this.sessionInfoManager.get(currentSessionId);
33
102
  if (sessionInfo === undefined ||
@@ -40,9 +109,14 @@ class ClientAuthentication extends ClientAuthentication$1 {
40
109
  this.handleIncomingRedirect = async (url, eventEmitter) => {
41
110
  try {
42
111
  const redirectInfo = await this.redirectHandler.handle(url, eventEmitter);
112
+ // The `FallbackRedirectHandler` directly returns the global `fetch` for
113
+ // his value, so we should ensure it's bound to `window` rather than to
114
+ // ClientAuthentication, to avoid the following error:
115
+ // > 'fetch' called on an object that does not implement interface Window.
43
116
  this.fetch = redirectInfo.fetch.bind(window);
44
117
  this.boundLogout = redirectInfo.getLogoutUrl;
45
- this.cleanUrlAfterRedirect(url);
118
+ // Strip the oauth params:
119
+ await this.cleanUrlAfterRedirect(url);
46
120
  return {
47
121
  isLoggedIn: redirectInfo.isLoggedIn,
48
122
  webId: redirectInfo.webId,
@@ -51,31 +125,69 @@ class ClientAuthentication extends ClientAuthentication$1 {
51
125
  };
52
126
  }
53
127
  catch (err) {
54
- this.cleanUrlAfterRedirect(url);
128
+ // Strip the oauth params:
129
+ await this.cleanUrlAfterRedirect(url);
130
+ // FIXME: EVENTS.ERROR should be errorCode, errorDescription
131
+ //
132
+ // I'm not sure if "redirect" is a good error code, and in theory `err`
133
+ // maybe an Error object and not a string; Maybe we want to just hardcode
134
+ // a description instead?
55
135
  eventEmitter.emit(EVENTS.ERROR, "redirect", err);
56
136
  return undefined;
57
137
  }
58
138
  };
59
139
  }
60
- cleanUrlAfterRedirect(url) {
61
- const cleanedUpUrl = new URL(url);
62
- cleanedUpUrl.searchParams.delete("state");
63
- cleanedUpUrl.searchParams.delete("code");
64
- cleanedUpUrl.searchParams.delete("id_token");
65
- cleanedUpUrl.searchParams.delete("access_token");
66
- cleanedUpUrl.searchParams.delete("error");
67
- cleanedUpUrl.searchParams.delete("error_description");
68
- cleanedUpUrl.searchParams.delete("iss");
69
- window.history.replaceState(null, "", cleanedUpUrl.toString());
140
+ async cleanUrlAfterRedirect(url) {
141
+ const cleanedUpUrl = removeOpenIdParams(url).href;
142
+ // Remove OAuth-specific query params (since the login flow finishes with
143
+ // the browser being redirected back with OAuth2 query params (e.g. for
144
+ // 'code' and 'state'), and so if the user simply refreshes this page our
145
+ // authentication library will be called again with what are now invalid
146
+ // query parameters!).
147
+ window.history.replaceState(null, "", cleanedUpUrl);
148
+ while (window.location.href !== cleanedUpUrl) {
149
+ // Poll the current URL every ms. Active polling is required because
150
+ // window.history.replaceState is asynchronous, but the associated
151
+ // 'popstate' event which should be listened to is only sent on active
152
+ // navigation, which we will not have here.
153
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
154
+ // eslint-disable-next-line no-await-in-loop
155
+ await new Promise((resolve) => {
156
+ setTimeout(() => resolve(), 1);
157
+ });
158
+ }
70
159
  }
71
160
  }
72
161
 
162
+ //
163
+ // Copyright Inrupt Inc.
164
+ //
165
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
166
+ // of this software and associated documentation files (the "Software"), to deal in
167
+ // the Software without restriction, including without limitation the rights to use,
168
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
169
+ // Software, and to permit persons to whom the Software is furnished to do so,
170
+ // subject to the following conditions:
171
+ //
172
+ // The above copyright notice and this permission notice shall be included in
173
+ // all copies or substantial portions of the Software.
174
+ //
175
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
176
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
177
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
178
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
179
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
180
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
181
+ //
73
182
  function hasIssuer(options) {
74
183
  return typeof options.oidcIssuer === "string";
75
184
  }
76
185
  function hasRedirectUrl(options) {
77
186
  return typeof options.redirectUrl === "string";
78
187
  }
188
+ /**
189
+ * @hidden
190
+ */
79
191
  class OidcLoginHandler {
80
192
  constructor(storageUtility, oidcHandler, issuerConfigFetcher, clientRegistrar) {
81
193
  this.storageUtility = storageUtility;
@@ -97,22 +209,57 @@ class OidcLoginHandler {
97
209
  if (!hasRedirectUrl(options)) {
98
210
  throw new ConfigurationError(`OidcLoginHandler requires a redirect URL: missing property 'redirectUrl' in ${JSON.stringify(options)}`);
99
211
  }
212
+ // Fetch issuer config.
100
213
  const issuerConfig = await this.issuerConfigFetcher.fetchConfig(options.oidcIssuer);
101
214
  const clientRegistration = await handleRegistration(options, issuerConfig, this.storageUtility, this.clientRegistrar);
215
+ // Construct OIDC Options
102
216
  const OidcOptions = {
217
+ // Note that here, the issuer is not the one from the received options, but
218
+ // from the issuer's config. This enforces the canonical URL is used and stored,
219
+ // which is also the one present in the ID token, so storing a technically
220
+ // valid, but different issuer URL (e.g. using a trailing slash or not) now
221
+ // could prevent from validating the ID token later.
103
222
  issuer: issuerConfig.issuer,
223
+ // TODO: differentiate if DPoP should be true
104
224
  dpop: options.tokenType.toLowerCase() === "dpop",
105
225
  ...options,
106
226
  issuerConfiguration: issuerConfig,
107
227
  client: clientRegistration,
108
228
  };
229
+ // Call proper OIDC Handler
109
230
  return this.oidcHandler.handle(OidcOptions);
110
231
  }
111
232
  }
112
233
 
234
+ //
235
+ // Copyright Inrupt Inc.
236
+ //
237
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
238
+ // of this software and associated documentation files (the "Software"), to deal in
239
+ // the Software without restriction, including without limitation the rights to use,
240
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
241
+ // Software, and to permit persons to whom the Software is furnished to do so,
242
+ // subject to the following conditions:
243
+ //
244
+ // The above copyright notice and this permission notice shall be included in
245
+ // all copies or substantial portions of the Software.
246
+ //
247
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
248
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
249
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
250
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
251
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
252
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
253
+ //
254
+ /**
255
+ * @hidden
256
+ * Authorization code flow spec: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
257
+ * PKCE: https://tools.ietf.org/html/rfc7636
258
+ */
113
259
  class AuthorizationCodeWithPkceOidcHandler extends AuthorizationCodeWithPkceOidcHandlerBase {
114
260
  async handle(oidcLoginOptions) {
115
261
  var _a;
262
+ /* eslint-disable camelcase */
116
263
  const oidcOptions = {
117
264
  authority: oidcLoginOptions.issuer.toString(),
118
265
  client_id: oidcLoginOptions.client.clientId,
@@ -122,28 +269,58 @@ class AuthorizationCodeWithPkceOidcHandler extends AuthorizationCodeWithPkceOidc
122
269
  response_type: "code",
123
270
  scope: DEFAULT_SCOPES,
124
271
  filterProtocolClaims: true,
272
+ // The userinfo endpoint on NSS fails, so disable this for now
273
+ // Note that in Solid, information should be retrieved from the
274
+ // profile referenced by the WebId.
125
275
  loadUserInfo: false,
126
276
  code_verifier: true,
127
277
  prompt: (_a = oidcLoginOptions.prompt) !== null && _a !== void 0 ? _a : "consent",
128
278
  };
279
+ /* eslint-enable camelcase */
129
280
  const oidcClientLibrary = new OidcClient(oidcOptions);
130
281
  try {
131
282
  const signingRequest = await oidcClientLibrary.createSigninRequest();
283
+ // Make sure to await the promise before returning so that the error is caught.
132
284
  return await this.handleRedirect({
133
285
  oidcLoginOptions,
286
+ // eslint-disable-next-line no-underscore-dangle
134
287
  state: signingRequest.state._id,
288
+ // eslint-disable-next-line no-underscore-dangle
135
289
  codeVerifier: signingRequest.state._code_verifier,
136
290
  targetUrl: signingRequest.url.toString(),
137
291
  });
138
292
  }
139
293
  catch (err) {
294
+ // eslint-disable-next-line no-console
140
295
  console.error(err);
141
296
  }
297
+ // The login is only completed AFTER redirect, so nothing to return here.
142
298
  return undefined;
143
299
  }
144
300
  }
145
301
 
302
+ //
303
+ // Copyright Inrupt Inc.
304
+ //
305
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
306
+ // of this software and associated documentation files (the "Software"), to deal in
307
+ // the Software without restriction, including without limitation the rights to use,
308
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
309
+ // Software, and to permit persons to whom the Software is furnished to do so,
310
+ // subject to the following conditions:
311
+ //
312
+ // The above copyright notice and this permission notice shall be included in
313
+ // all copies or substantial portions of the Software.
314
+ //
315
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
316
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
317
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
318
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
319
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
320
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
321
+ //
146
322
  const WELL_KNOWN_OPENID_CONFIG = ".well-known/openid-configuration";
323
+ /* eslint-disable camelcase */
147
324
  const issuerConfigKeyMap = {
148
325
  issuer: {
149
326
  toKey: "issuer",
@@ -231,10 +408,15 @@ const issuerConfigKeyMap = {
231
408
  convertToUrl: true,
232
409
  },
233
410
  };
411
+ /* eslint-enable camelcase */
234
412
  function processConfig(config) {
235
413
  const parsedConfig = {};
236
414
  Object.keys(config).forEach((key) => {
237
415
  if (issuerConfigKeyMap[key]) {
416
+ // TODO: PMcB55: Validate URL if "issuerConfigKeyMap[key].convertToUrl"
417
+ // if (issuerConfigKeyMap[key].convertToUrl) {
418
+ // validateUrl(config[key]);
419
+ // }
238
420
  parsedConfig[issuerConfigKeyMap[key].toKey] = config[key];
239
421
  }
240
422
  });
@@ -243,33 +425,71 @@ function processConfig(config) {
243
425
  }
244
426
  return parsedConfig;
245
427
  }
428
+ /**
429
+ * @hidden
430
+ */
246
431
  class IssuerConfigFetcher {
247
432
  constructor(storageUtility) {
248
433
  this.storageUtility = storageUtility;
249
434
  this.storageUtility = storageUtility;
250
435
  }
436
+ // This method needs no state (so can be static), and can be exposed to allow
437
+ // callers to know where this implementation puts state it needs.
251
438
  static getLocalStorageKey(issuer) {
252
439
  return `issuerConfig:${issuer}`;
253
440
  }
254
441
  async fetchConfig(issuer) {
255
442
  let issuerConfig;
256
- const openIdConfigUrl = new URL(WELL_KNOWN_OPENID_CONFIG, issuer.endsWith("/") ? issuer : `${issuer}/`).href;
443
+ const openIdConfigUrl = new URL(WELL_KNOWN_OPENID_CONFIG,
444
+ // Make sure to append a slash at issuer URL, so that the .well-known URL
445
+ // includes the full issuer path. See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig.
446
+ issuer.endsWith("/") ? issuer : `${issuer}/`).href;
257
447
  const issuerConfigRequestBody = await fetch$1.call(globalThis, openIdConfigUrl);
448
+ // Check the validity of the fetched config
258
449
  try {
259
450
  issuerConfig = processConfig(await issuerConfigRequestBody.json());
260
451
  }
261
452
  catch (err) {
262
453
  throw new ConfigurationError(`[${issuer.toString()}] has an invalid configuration: ${err.message}`);
263
454
  }
455
+ // Update store with fetched config
264
456
  await this.storageUtility.set(IssuerConfigFetcher.getLocalStorageKey(issuer), JSON.stringify(issuerConfig));
265
457
  return issuerConfig;
266
458
  }
267
459
  }
268
460
 
461
+ //
462
+ // Copyright Inrupt Inc.
463
+ //
464
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
465
+ // of this software and associated documentation files (the "Software"), to deal in
466
+ // the Software without restriction, including without limitation the rights to use,
467
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
468
+ // Software, and to permit persons to whom the Software is furnished to do so,
469
+ // subject to the following conditions:
470
+ //
471
+ // The above copyright notice and this permission notice shall be included in
472
+ // all copies or substantial portions of the Software.
473
+ //
474
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
475
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
476
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
477
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
478
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
479
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
480
+ //
481
+ /**
482
+ * @param sessionId
483
+ * @param storage
484
+ * @hidden
485
+ */
269
486
  async function clear(sessionId, storage) {
270
487
  await clear$1(sessionId, storage);
271
488
  await clearOidcPersistentStorage();
272
489
  }
490
+ /**
491
+ * @hidden
492
+ */
273
493
  class SessionInfoManager extends SessionInfoManagerBase {
274
494
  async get(sessionId) {
275
495
  const [isLoggedIn, webId, clientId, clientSecret, redirectUrl, refreshToken, issuer, tokenType,] = await Promise.all([
@@ -299,6 +519,9 @@ class SessionInfoManager extends SessionInfoManagerBase {
299
519
  }),
300
520
  ]);
301
521
  if (typeof redirectUrl === "string" && !isValidRedirectUrl(redirectUrl)) {
522
+ // This resolves the issue for people experiencing https://github.com/inrupt/solid-client-authn-js/issues/2891.
523
+ // An invalid redirect URL is present in the storage, and the session should
524
+ // be cleared to get a fresh start. This will require the user to log back in.
302
525
  await Promise.all([
303
526
  this.storageUtility.deleteAllUserData(sessionId, { secure: false }),
304
527
  this.storageUtility.deleteAllUserData(sessionId, { secure: true }),
@@ -323,17 +546,52 @@ class SessionInfoManager extends SessionInfoManagerBase {
323
546
  issuer,
324
547
  clientAppId: clientId,
325
548
  clientAppSecret: clientSecret,
549
+ // Default the token type to DPoP if unspecified.
326
550
  tokenType: tokenType !== null && tokenType !== void 0 ? tokenType : "DPoP",
327
551
  };
328
552
  }
553
+ /**
554
+ * This function removes all session-related information from storage.
555
+ * @param sessionId the session identifier
556
+ * @param storage the storage where session info is stored
557
+ * @hidden
558
+ */
329
559
  async clear(sessionId) {
330
560
  return clear(sessionId, this.storageUtility);
331
561
  }
332
562
  }
333
563
 
564
+ //
565
+ // Copyright Inrupt Inc.
566
+ //
567
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
568
+ // of this software and associated documentation files (the "Software"), to deal in
569
+ // the Software without restriction, including without limitation the rights to use,
570
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
571
+ // Software, and to permit persons to whom the Software is furnished to do so,
572
+ // subject to the following conditions:
573
+ //
574
+ // The above copyright notice and this permission notice shall be included in
575
+ // all copies or substantial portions of the Software.
576
+ //
577
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
578
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
579
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
580
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
581
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
582
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
583
+ //
584
+ /**
585
+ * This class handles redirect IRIs without any query params, and returns an unauthenticated
586
+ * session. It serves as a fallback so that consuming libraries don't have to test
587
+ * for the query params themselves, and can always try to use them as a redirect IRI.
588
+ * @hidden
589
+ */
334
590
  class FallbackRedirectHandler {
335
591
  async canHandle(redirectUrl) {
336
592
  try {
593
+ // The next URL object is built for validating it.
594
+ // eslint-disable-next-line no-new
337
595
  new URL(redirectUrl);
338
596
  return true;
339
597
  }
@@ -341,12 +599,44 @@ class FallbackRedirectHandler {
341
599
  throw new Error(`[${redirectUrl}] is not a valid URL, and cannot be used as a redirect URL: ${e}`);
342
600
  }
343
601
  }
344
- async handle(_redirectUrl) {
602
+ async handle(
603
+ // The argument is ignored, but must be present to implement the interface
604
+ _redirectUrl) {
345
605
  return getUnauthenticatedSession();
346
606
  }
347
607
  }
348
608
 
609
+ //
610
+ // Copyright Inrupt Inc.
611
+ //
612
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
613
+ // of this software and associated documentation files (the "Software"), to deal in
614
+ // the Software without restriction, including without limitation the rights to use,
615
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
616
+ // Software, and to permit persons to whom the Software is furnished to do so,
617
+ // subject to the following conditions:
618
+ //
619
+ // The above copyright notice and this permission notice shall be included in
620
+ // all copies or substantial portions of the Software.
621
+ //
622
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
623
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
624
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
625
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
626
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
627
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
628
+ //
629
+ /**
630
+ * @hidden
631
+ * @packageDocumentation
632
+ */
633
+ // FIXME: The following doesn't work in the browser, it results in all the fetches
634
+ // being unauthenticated. This should be looked into when migrating to universal-fetch.
635
+ // import { fetch } from "cross-fetch";
349
636
  const globalFetch = (...args) => fetch$1.call(globalThis, ...args);
637
+ /**
638
+ * @hidden
639
+ */
350
640
  class AuthCodeRedirectHandler {
351
641
  constructor(storageUtility, sessionInfoManager, issuerConfigFetcher, clientRegistrar, tokerRefresher) {
352
642
  this.storageUtility = storageUtility;
@@ -396,10 +686,15 @@ class AuthCodeRedirectHandler {
396
686
  if (isDpop) {
397
687
  tokens = await getDpopToken(issuerConfig, client, {
398
688
  grantType: "authorization_code",
689
+ // We rely on our 'canHandle' function checking that the OAuth 'code'
690
+ // parameter is present in our query string.
399
691
  code: url.searchParams.get("code"),
400
692
  codeVerifier,
401
693
  redirectUrl: storedRedirectIri,
402
694
  });
695
+ // Delete oidc-client-specific session information from storage. This is
696
+ // done automatically when retrieving a bearer token, but since the DPoP
697
+ // binding uses our custom code, this needs to be done manually.
403
698
  window.localStorage.removeItem(`oidc.${oauthState}`);
404
699
  }
405
700
  else {
@@ -440,12 +735,58 @@ class AuthCodeRedirectHandler {
440
735
  }
441
736
  }
442
737
 
738
+ //
739
+ // Copyright Inrupt Inc.
740
+ //
741
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
742
+ // of this software and associated documentation files (the "Software"), to deal in
743
+ // the Software without restriction, including without limitation the rights to use,
744
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
745
+ // Software, and to permit persons to whom the Software is furnished to do so,
746
+ // subject to the following conditions:
747
+ //
748
+ // The above copyright notice and this permission notice shall be included in
749
+ // all copies or substantial portions of the Software.
750
+ //
751
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
752
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
753
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
754
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
755
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
756
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
757
+ //
758
+ /**
759
+ * @hidden
760
+ */
443
761
  class AggregateRedirectHandler extends AggregateHandler {
444
762
  constructor(redirectHandlers) {
445
763
  super(redirectHandlers);
446
764
  }
447
765
  }
448
766
 
767
+ //
768
+ // Copyright Inrupt Inc.
769
+ //
770
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
771
+ // of this software and associated documentation files (the "Software"), to deal in
772
+ // the Software without restriction, including without limitation the rights to use,
773
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
774
+ // Software, and to permit persons to whom the Software is furnished to do so,
775
+ // subject to the following conditions:
776
+ //
777
+ // The above copyright notice and this permission notice shall be included in
778
+ // all copies or substantial portions of the Software.
779
+ //
780
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
781
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
782
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
783
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
784
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
785
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
786
+ //
787
+ /**
788
+ * @hidden
789
+ */
449
790
  class BrowserStorage {
450
791
  get storage() {
451
792
  return window.localStorage;
@@ -461,6 +802,29 @@ class BrowserStorage {
461
802
  }
462
803
  }
463
804
 
805
+ //
806
+ // Copyright Inrupt Inc.
807
+ //
808
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
809
+ // of this software and associated documentation files (the "Software"), to deal in
810
+ // the Software without restriction, including without limitation the rights to use,
811
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
812
+ // Software, and to permit persons to whom the Software is furnished to do so,
813
+ // subject to the following conditions:
814
+ //
815
+ // The above copyright notice and this permission notice shall be included in
816
+ // all copies or substantial portions of the Software.
817
+ //
818
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
819
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
820
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
821
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
822
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
823
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
824
+ //
825
+ /**
826
+ * @hidden
827
+ */
464
828
  class Redirector {
465
829
  redirect(redirectUrl, options) {
466
830
  if (options && options.handleRedirect) {
@@ -475,19 +839,49 @@ class Redirector {
475
839
  }
476
840
  }
477
841
 
842
+ //
843
+ // Copyright Inrupt Inc.
844
+ //
845
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
846
+ // of this software and associated documentation files (the "Software"), to deal in
847
+ // the Software without restriction, including without limitation the rights to use,
848
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
849
+ // Software, and to permit persons to whom the Software is furnished to do so,
850
+ // subject to the following conditions:
851
+ //
852
+ // The above copyright notice and this permission notice shall be included in
853
+ // all copies or substantial portions of the Software.
854
+ //
855
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
856
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
857
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
858
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
859
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
860
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
861
+ //
862
+ /**
863
+ * @hidden
864
+ */
478
865
  class ClientRegistrar {
479
866
  constructor(storageUtility) {
480
867
  this.storageUtility = storageUtility;
481
868
  this.storageUtility = storageUtility;
482
869
  }
483
870
  async getClient(options, issuerConfig) {
484
- const [storedClientId, storedClientSecret,] = await Promise.all([
871
+ // If client secret and/or client id are stored in storage, use those.
872
+ const [storedClientId, storedClientSecret,
873
+ // storedClientName,
874
+ ] = await Promise.all([
485
875
  this.storageUtility.getForUser(options.sessionId, "clientId", {
486
876
  secure: false,
487
877
  }),
488
878
  this.storageUtility.getForUser(options.sessionId, "clientSecret", {
489
879
  secure: false,
490
880
  }),
881
+ // this.storageUtility.getForUser(options.sessionId, "clientName", {
882
+ // // FIXME: figure out how to persist secure storage at reload
883
+ // secure: false,
884
+ // }),
491
885
  ]);
492
886
  if (storedClientId) {
493
887
  return {
@@ -498,6 +892,7 @@ class ClientRegistrar {
498
892
  }
499
893
  try {
500
894
  const registeredClient = await registerClient(options, issuerConfig);
895
+ // Save info
501
896
  const infoToSave = {
502
897
  clientId: registeredClient.clientId,
503
898
  };
@@ -509,6 +904,9 @@ class ClientRegistrar {
509
904
  registeredClient.idTokenSignedResponseAlg;
510
905
  }
511
906
  await this.storageUtility.setForUser(options.sessionId, infoToSave, {
907
+ // FIXME: figure out how to persist secure storage at reload
908
+ // Otherwise, the client info cannot be retrieved from storage, and
909
+ // the lib tries to re-register the client on each fetch
512
910
  secure: false,
513
911
  });
514
912
  return registeredClient;
@@ -519,9 +917,36 @@ class ClientRegistrar {
519
917
  }
520
918
  }
521
919
 
920
+ //
921
+ // Copyright Inrupt Inc.
922
+ //
923
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
924
+ // of this software and associated documentation files (the "Software"), to deal in
925
+ // the Software without restriction, including without limitation the rights to use,
926
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
927
+ // Software, and to permit persons to whom the Software is furnished to do so,
928
+ // subject to the following conditions:
929
+ //
930
+ // The above copyright notice and this permission notice shall be included in
931
+ // all copies or substantial portions of the Software.
932
+ //
933
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
934
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
935
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
936
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
937
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
938
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
939
+ //
940
+ /**
941
+ * This class handles redirect IRIs without any query params, and returns an unauthenticated
942
+ * session. It serves as a fallback so that consuming libraries don't have to test
943
+ * for the query params themselves, and can always try to use them as a redirect IRI.
944
+ * @hidden
945
+ */
522
946
  class ErrorOidcHandler {
523
947
  async canHandle(redirectUrl) {
524
948
  try {
949
+ // eslint-disable-next-line no-new
525
950
  return new URL(redirectUrl).searchParams.has("error");
526
951
  }
527
952
  catch (e) {
@@ -539,6 +964,32 @@ class ErrorOidcHandler {
539
964
  }
540
965
  }
541
966
 
967
+ //
968
+ // Copyright Inrupt Inc.
969
+ //
970
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
971
+ // of this software and associated documentation files (the "Software"), to deal in
972
+ // the Software without restriction, including without limitation the rights to use,
973
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
974
+ // Software, and to permit persons to whom the Software is furnished to do so,
975
+ // subject to the following conditions:
976
+ //
977
+ // The above copyright notice and this permission notice shall be included in
978
+ // all copies or substantial portions of the Software.
979
+ //
980
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
981
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
982
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
983
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
984
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
985
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
986
+ //
987
+ // Some identifiers are not in camelcase on purpose, as they are named using the
988
+ // official names from the OIDC/OAuth2 specifications.
989
+ /* eslint-disable camelcase */
990
+ /**
991
+ * @hidden
992
+ */
542
993
  class TokenRefresher {
543
994
  constructor(storageUtility, issuerConfigFetcher, clientRegistrar) {
544
995
  this.storageUtility = storageUtility;
@@ -550,8 +1001,10 @@ class TokenRefresher {
550
1001
  }
551
1002
  async refresh(sessionId, refreshToken, dpopKey, eventEmitter) {
552
1003
  const oidcContext = await loadOidcContextFromStorage(sessionId, this.storageUtility, this.issuerConfigFetcher);
1004
+ // This should also retrieve the client from storage
553
1005
  const clientInfo = await this.clientRegistrar.getClient({ sessionId }, oidcContext.issuerConfig);
554
1006
  if (refreshToken === undefined) {
1007
+ // TODO: in a next PR, look up storage for a refresh token
555
1008
  throw new Error(`Session [${sessionId}] has no refresh token to allow it to refresh its access token.`);
556
1009
  }
557
1010
  if (oidcContext.dpop && dpopKey === undefined) {
@@ -568,6 +1021,31 @@ class TokenRefresher {
568
1021
  }
569
1022
  }
570
1023
 
1024
+ //
1025
+ // Copyright Inrupt Inc.
1026
+ //
1027
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
1028
+ // of this software and associated documentation files (the "Software"), to deal in
1029
+ // the Software without restriction, including without limitation the rights to use,
1030
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
1031
+ // Software, and to permit persons to whom the Software is furnished to do so,
1032
+ // subject to the following conditions:
1033
+ //
1034
+ // The above copyright notice and this permission notice shall be included in
1035
+ // all copies or substantial portions of the Software.
1036
+ //
1037
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
1038
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
1039
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1040
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1041
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1042
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1043
+ //
1044
+ /**
1045
+ *
1046
+ * @param dependencies
1047
+ * @deprecated This function will be removed from the external API in an upcoming release.
1048
+ */
571
1049
  function getClientAuthenticationWithDependencies(dependencies) {
572
1050
  const inMemoryStorage = new InMemoryStorage();
573
1051
  const secureStorage = dependencies.secureStorage || inMemoryStorage;
@@ -578,22 +1056,69 @@ function getClientAuthenticationWithDependencies(dependencies) {
578
1056
  const sessionInfoManager = new SessionInfoManager(storageUtility);
579
1057
  const tokenRefresher = new TokenRefresher(storageUtility, issuerConfigFetcher, clientRegistrar);
580
1058
  const redirector = new Redirector();
1059
+ // make new handler for redirect and login
581
1060
  const loginHandler = new OidcLoginHandler(storageUtility, new AuthorizationCodeWithPkceOidcHandler(storageUtility, redirector), issuerConfigFetcher, clientRegistrar);
582
1061
  const redirectHandler = new AggregateRedirectHandler([
583
1062
  new ErrorOidcHandler(),
584
1063
  new AuthCodeRedirectHandler(storageUtility, sessionInfoManager, issuerConfigFetcher, clientRegistrar, tokenRefresher),
1064
+ // This catch-all class will always be able to handle the
1065
+ // redirect IRI, so it must be registered last.
585
1066
  new FallbackRedirectHandler(),
586
1067
  ]);
587
1068
  return new ClientAuthentication(loginHandler, redirectHandler, new IWaterfallLogoutHandler(sessionInfoManager, redirector), sessionInfoManager, issuerConfigFetcher);
588
1069
  }
589
1070
 
1071
+ //
1072
+ // Copyright Inrupt Inc.
1073
+ //
1074
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
1075
+ // of this software and associated documentation files (the "Software"), to deal in
1076
+ // the Software without restriction, including without limitation the rights to use,
1077
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
1078
+ // Software, and to permit persons to whom the Software is furnished to do so,
1079
+ // subject to the following conditions:
1080
+ //
1081
+ // The above copyright notice and this permission notice shall be included in
1082
+ // all copies or substantial portions of the Software.
1083
+ //
1084
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
1085
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
1086
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1087
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1088
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1089
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1090
+ //
590
1091
  const KEY_CURRENT_SESSION = `${SOLID_CLIENT_AUTHN_KEY_PREFIX}currentSession`;
591
1092
  const KEY_CURRENT_URL = `${SOLID_CLIENT_AUTHN_KEY_PREFIX}currentUrl`;
592
1093
 
1094
+ //
1095
+ // Copyright Inrupt Inc.
1096
+ //
1097
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
1098
+ // of this software and associated documentation files (the "Software"), to deal in
1099
+ // the Software without restriction, including without limitation the rights to use,
1100
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
1101
+ // Software, and to permit persons to whom the Software is furnished to do so,
1102
+ // subject to the following conditions:
1103
+ //
1104
+ // The above copyright notice and this permission notice shall be included in
1105
+ // all copies or substantial portions of the Software.
1106
+ //
1107
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
1108
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
1109
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1110
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1111
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1112
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1113
+ //
593
1114
  async function silentlyAuthenticate(sessionId, clientAuthn, session) {
594
1115
  var _a;
595
1116
  const storedSessionInfo = await clientAuthn.validateCurrentSession(sessionId);
596
1117
  if (storedSessionInfo !== null) {
1118
+ // It can be really useful to save the user's current browser location,
1119
+ // so that we can restore it after completing the silent authentication
1120
+ // on incoming redirect. This way, the user is eventually redirected back
1121
+ // to the page they were on and not to the app's redirect page.
597
1122
  window.localStorage.setItem(KEY_CURRENT_URL, window.location.href);
598
1123
  await clientAuthn.login({
599
1124
  sessionId,
@@ -611,21 +1136,68 @@ async function silentlyAuthenticate(sessionId, clientAuthn, session) {
611
1136
  function isLoggedIn(sessionInfo) {
612
1137
  return !!(sessionInfo === null || sessionInfo === void 0 ? void 0 : sessionInfo.isLoggedIn);
613
1138
  }
1139
+ /**
1140
+ * A {@link Session} object represents a user's session on an application. The session holds state, as it stores information enabling acces to private resources after login for instance.
1141
+ */
614
1142
  class Session extends EventEmitter {
1143
+ /**
1144
+ * Session object constructor. Typically called as follows:
1145
+ *
1146
+ * ```typescript
1147
+ * const session = new Session();
1148
+ * ```
1149
+ *
1150
+ * See also [getDefaultSession](https://docs.inrupt.com/developer-tools/api/javascript/solid-client-authn-browser/functions.html#getdefaultsession).
1151
+ *
1152
+ * @param sessionOptions The options enabling the correct instantiation of
1153
+ * the session. Either both storages or clientAuthentication are required. For
1154
+ * more information, see {@link ISessionOptions}.
1155
+ * @param sessionId A string uniquely identifying the session.
1156
+ *
1157
+ */
615
1158
  constructor(sessionOptions = {}, sessionId = undefined) {
616
1159
  super();
617
1160
  this.tokenRequestInProgress = false;
1161
+ /**
1162
+ * Triggers the login process. Note that this method will redirect the user away from your app.
1163
+ *
1164
+ * @param options Parameter to customize the login behaviour. In particular, two options are mandatory: `options.oidcIssuer`, the user's identity provider, and `options.redirectUrl`, the URL to which the user will be redirected after logging in their identity provider.
1165
+ * @returns This method should redirect the user away from the app: it does not return anything. The login process is completed by {@linkcode handleIncomingRedirect}.
1166
+ */
1167
+ // Define these functions as properties so that they don't get accidentally re-bound.
1168
+ // Isn't Javascript fun?
618
1169
  this.login = async (options) => {
619
1170
  var _a;
620
1171
  await this.clientAuthentication.login({
621
1172
  sessionId: this.info.sessionId,
622
1173
  ...options,
1174
+ // Defaults the token type to DPoP
623
1175
  tokenType: (_a = options.tokenType) !== null && _a !== void 0 ? _a : "DPoP",
624
1176
  }, this.events);
1177
+ // `login` redirects the user away from the app,
1178
+ // so unless it throws an error, there is no code that should run afterwards
1179
+ // (since there is no "after" in the lifetime of the script).
1180
+ // Hence, this Promise never resolves:
625
1181
  return new Promise(() => { });
626
1182
  };
1183
+ /**
1184
+ * Fetches data using available login information. If the user is not logged in, this will behave as a regular `fetch`. The signature of this method is identical to the [canonical `fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
1185
+ *
1186
+ * @param url The URL from which data should be fetched.
1187
+ * @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/).
1188
+ */
627
1189
  this.fetch = (url, init) => this.clientAuthentication.fetch(url, init);
1190
+ /**
1191
+ * An internal logout function, to control whether or not the logout signal
1192
+ * should be sent, i.e. if the logout was user-initiated or is the result of
1193
+ * an external event.
1194
+ *
1195
+ * @hidden
1196
+ */
628
1197
  this.internalLogout = async (emitSignal, options) => {
1198
+ // Clearing this value means that silent refresh will no longer be attempted.
1199
+ // In particular, in the case of a silent authentication error it prevents
1200
+ // from getting stuck in an authentication retries loop.
629
1201
  window.localStorage.removeItem(KEY_CURRENT_SESSION);
630
1202
  await this.clientAuthentication.logout(this.info.sessionId, options);
631
1203
  this.info.isLoggedIn = false;
@@ -633,7 +1205,50 @@ class Session extends EventEmitter {
633
1205
  this.events.emit(EVENTS.LOGOUT);
634
1206
  }
635
1207
  };
1208
+ /**
1209
+ * Logs the user out of the application.
1210
+ *
1211
+ * There are 2 types of logout supported by this library,
1212
+ * `app` logout and `idp` logout.
1213
+ *
1214
+ * App logout will log the user out within the application
1215
+ * by clearing any session data from the browser. It does
1216
+ * not log the user out of their Solid identity provider,
1217
+ * and should not redirect the user away.
1218
+ * App logout can be performed as follows:
1219
+ * ```typescript
1220
+ * await session.logout({ logoutType: 'app' });
1221
+ * ```
1222
+ *
1223
+ * IDP logout will log the user out of their Solid identity provider,
1224
+ * and will redirect the user away from the application to do so. In order
1225
+ * for users to be redirected back to `postLogoutUrl` you MUST include the
1226
+ * `postLogoutUrl` value in the `post_logout_redirect_uris` field in the
1227
+ * [Client ID Document](https://docs.inrupt.com/ess/latest/security/authentication/#client-identifier-client-id).
1228
+ * IDP logout can be performed as follows:
1229
+ * ```typescript
1230
+ * await session.logout({
1231
+ * logoutType: 'idp',
1232
+ * // An optional URL to redirect to after logout has completed;
1233
+ * // this MUST match a logout URL listed in the Client ID Document
1234
+ * // of the application that is logged in.
1235
+ * // If the application is logged in with a Client ID that is not
1236
+ * // a URI dereferencing to a Client ID Document then users will
1237
+ * // not be redirected back to the `postLogoutUrl` after logout.
1238
+ * postLogoutUrl: 'https://example.com/logout',
1239
+ * // An optional value to be included in the query parameters
1240
+ * // when the IDP provider redirects the user to the postLogoutRedirectUrl.
1241
+ * state: "my-state"
1242
+ * });
1243
+ * ```
1244
+ */
636
1245
  this.logout = async (options) => this.internalLogout(true, options);
1246
+ /**
1247
+ * Completes the login process by processing the information provided by the
1248
+ * Solid identity provider through redirect.
1249
+ *
1250
+ * @param options See {@see IHandleIncomingRedirectOptions}.
1251
+ */
637
1252
  this.handleIncomingRedirect = async (inputOptions = {}) => {
638
1253
  var _a;
639
1254
  if (this.info.isLoggedIn) {
@@ -650,17 +1265,33 @@ class Session extends EventEmitter {
650
1265
  this.setSessionInfo(sessionInfo);
651
1266
  const currentUrl = window.localStorage.getItem(KEY_CURRENT_URL);
652
1267
  if (currentUrl === null) {
1268
+ // The login event can only be triggered **after** the user has been
1269
+ // redirected from the IdP with access and ID tokens.
653
1270
  this.events.emit(EVENTS.LOGIN);
654
1271
  }
655
1272
  else {
1273
+ // If an URL is stored in local storage, we are being logged in after a
1274
+ // silent authentication, so remove our currently stored URL location
1275
+ // to clean up our state now that we are completing the re-login process.
656
1276
  window.localStorage.removeItem(KEY_CURRENT_URL);
657
1277
  this.events.emit(EVENTS.SESSION_RESTORED, currentUrl);
658
1278
  }
659
1279
  }
660
1280
  else if (options.restorePreviousSession === true) {
1281
+ // Silent authentication happens after a refresh, which means there are no
1282
+ // OAuth params in the current location IRI. It can only succeed if a session
1283
+ // was previously logged in, in which case its ID will be present with a known
1284
+ // identifier in local storage.
1285
+ // Check if we have a locally stored session ID...
661
1286
  const storedSessionId = window.localStorage.getItem(KEY_CURRENT_SESSION);
1287
+ // ...if not, then there is no ID token, and so silent authentication cannot happen, but
1288
+ // if we do have a stored session ID, attempt to re-authenticate now silently.
662
1289
  if (storedSessionId !== null) {
663
1290
  const attemptedSilentAuthentication = await silentlyAuthenticate(storedSessionId, this.clientAuthentication, this);
1291
+ // At this point, we know that the main window will imminently be redirected.
1292
+ // However, this redirect is asynchronous and there is no way to halt execution
1293
+ // until it happens precisely. That's why the current Promise simply does not
1294
+ // resolve.
664
1295
  if (attemptedSilentAuthentication) {
665
1296
  return new Promise(() => { });
666
1297
  }
@@ -669,6 +1300,10 @@ class Session extends EventEmitter {
669
1300
  this.tokenRequestInProgress = false;
670
1301
  return sessionInfo;
671
1302
  };
1303
+ // Until Session no longer implements EventEmitter, this.events is just a proxy
1304
+ // to this (with some interface filtering). When we make the breaking change,
1305
+ // this.events will be a regular EventEmitter (implementing ISessionEventEmitter):
1306
+ // this.events = new EventEmitter();
672
1307
  this.events = new Proxy(this, buildProxyHandler(Session.prototype, "events only implements ISessionEventListener"));
673
1308
  if (sessionOptions.clientAuthentication) {
674
1309
  this.clientAuthentication = sessionOptions.clientAuthentication;
@@ -695,22 +1330,63 @@ class Session extends EventEmitter {
695
1330
  isLoggedIn: false,
696
1331
  };
697
1332
  }
1333
+ // When a session is logged in, we want to track its ID in local storage to
1334
+ // enable silent refresh. The current session ID specifically stored in 'localStorage'
1335
+ // (as opposed to using our storage abstraction layer) because it is only
1336
+ // used in a browser-specific mechanism.
698
1337
  this.events.on(EVENTS.LOGIN, () => window.localStorage.setItem(KEY_CURRENT_SESSION, this.info.sessionId));
699
1338
  this.events.on(EVENTS.SESSION_EXPIRED, () => this.internalLogout(false));
700
1339
  this.events.on(EVENTS.ERROR, () => this.internalLogout(false));
701
1340
  }
1341
+ /**
1342
+ * Register a callback function to be called when a user completes login.
1343
+ *
1344
+ * The callback is called when {@link handleIncomingRedirect} completes successfully.
1345
+ *
1346
+ * @param callback The function called when a user completes login.
1347
+ * @deprecated Prefer session.events.on(EVENTS.LOGIN, callback)
1348
+ */
702
1349
  onLogin(callback) {
703
1350
  this.events.on(EVENTS.LOGIN, callback);
704
1351
  }
1352
+ /**
1353
+ * Register a callback function to be called when a user logs out:
1354
+ *
1355
+ * @param callback The function called when a user completes logout.
1356
+ * @deprecated Prefer session.events.on(EVENTS.LOGOUT, callback)
1357
+ */
705
1358
  onLogout(callback) {
706
1359
  this.events.on(EVENTS.LOGOUT, callback);
707
1360
  }
1361
+ /**
1362
+ * Register a callback function to be called when a user logs out:
1363
+ *
1364
+ * @param callback The function called when an error occurs.
1365
+ * @since 1.11.0
1366
+ * @deprecated Prefer session.events.on(EVENTS.ERROR, callback)
1367
+ */
708
1368
  onError(callback) {
709
1369
  this.events.on(EVENTS.ERROR, callback);
710
1370
  }
1371
+ /**
1372
+ * Register a callback function to be called when a session is restored.
1373
+ *
1374
+ * Note: the callback will be called with the saved value of the 'current URL'
1375
+ * at the time the session was restored.
1376
+ *
1377
+ * @param callback The function called when a user's already logged-in session is restored, e.g., after a silent authentication is completed after a page refresh.
1378
+ * @deprecated Prefer session.events.on(EVENTS.SESSION_RESTORED, callback)
1379
+ */
711
1380
  onSessionRestore(callback) {
712
1381
  this.events.on(EVENTS.SESSION_RESTORED, callback);
713
1382
  }
1383
+ /**
1384
+ * Register a callback that runs when the session expires and can no longer
1385
+ * make authenticated requests, but following a user logout.
1386
+ * @param callback The function that runs on session expiration.
1387
+ * @since 1.11.0
1388
+ * @deprecated Prefer session.events.on(EVENTS.SESSION_EXPIRED, callback)
1389
+ */
714
1390
  onSessionExpiration(callback) {
715
1391
  this.events.on(EVENTS.SESSION_EXPIRED, callback);
716
1392
  }
@@ -725,41 +1401,132 @@ class Session extends EventEmitter {
725
1401
  }
726
1402
  }
727
1403
 
1404
+ //
1405
+ // Copyright Inrupt Inc.
1406
+ //
1407
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
1408
+ // of this software and associated documentation files (the "Software"), to deal in
1409
+ // the Software without restriction, including without limitation the rights to use,
1410
+ // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
1411
+ // Software, and to permit persons to whom the Software is furnished to do so,
1412
+ // subject to the following conditions:
1413
+ //
1414
+ // The above copyright notice and this permission notice shall be included in
1415
+ // all copies or substantial portions of the Software.
1416
+ //
1417
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
1418
+ // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
1419
+ // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1420
+ // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
1421
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1422
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1423
+ //
728
1424
  let defaultSession;
1425
+ /**
1426
+ * Obtain the {@link Session} used when not explicitly instantiating one yourself.
1427
+ *
1428
+ * When using the top-level exports {@link fetch}, {@link login}, {@link logout},
1429
+ * {@link handleIncomingRedirect}, {@link onLogin} and {@link onLogout}, these apply to an
1430
+ * implicitly-instantiated {@link Session}.
1431
+ * This function returns a reference to that Session in order to obtain e.g. the current user's
1432
+ * WebID.
1433
+ * @since 1.3.0
1434
+ */
729
1435
  function getDefaultSession() {
730
1436
  if (typeof defaultSession === "undefined") {
731
1437
  defaultSession = new Session();
732
1438
  }
733
1439
  return defaultSession;
734
1440
  }
1441
+ /**
1442
+ * This function's signature is equal to `window.fetch`, but if the current user is authenticated
1443
+ * (see [[login]] and [[handleIncomingRedirect]]), requests made using it will include that user's
1444
+ * credentials. If not, this will behave just like the regular `window.fetch`.
1445
+ *
1446
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch}
1447
+ * @since 1.3.0
1448
+ */
1449
+ /* eslint-disable-next-line no-shadow */
735
1450
  const fetch = (...args) => {
736
1451
  const session = getDefaultSession();
737
1452
  return session.fetch(...args);
738
1453
  };
1454
+ /**
1455
+ * Triggers the login process. Note that this method will redirect the user away from your app.
1456
+ *
1457
+ * @param options Parameter to customize the login behaviour. In particular, two options are mandatory: `options.oidcIssuer`, the user's identity provider, and `options.redirectUrl`, the URL to which the user will be redirected after logging in their identity provider.
1458
+ * @returns This method should redirect the user away from the app: it does not return anything. The login process is completed by [[handleIncomingRedirect]].
1459
+ * @since 1.3.0
1460
+ */
739
1461
  const login = (...args) => {
740
1462
  const session = getDefaultSession();
741
1463
  return session.login(...args);
742
1464
  };
1465
+ /**
1466
+ * Logs the user out of the application.
1467
+ *
1468
+ * By default this does not log the user out of their Solid identity provider.
1469
+ * In order to do so, you must set the logoutType to `idp`. For usage details
1470
+ * see {@link Session.logout}.
1471
+ *
1472
+ * @since 1.3.0
1473
+ */
743
1474
  const logout = (...args) => {
744
1475
  const session = getDefaultSession();
745
1476
  return session.logout(...args);
746
1477
  };
1478
+ /**
1479
+ * Completes the login process by processing the information provided by the Solid identity provider through redirect.
1480
+ *
1481
+ * @param url The URL of the page handling the redirect, including the query parameters — these contain the information to process the login.
1482
+ * @since 1.3.0
1483
+ */
747
1484
  const handleIncomingRedirect = (...args) => {
748
1485
  const session = getDefaultSession();
749
1486
  return session.handleIncomingRedirect(...args);
750
1487
  };
1488
+ /**
1489
+ * Register a callback function to be called when a user completes login.
1490
+ *
1491
+ * The callback is called when {@link handleIncomingRedirect} completes successfully.
1492
+ * @since 1.3.0
1493
+ *
1494
+ * @param callback The function called when a user completes login.
1495
+ * @deprecated Prefer events.on(EVENTS.LOGIN, callback)
1496
+
1497
+ */
751
1498
  const onLogin = (...args) => {
752
1499
  const session = getDefaultSession();
753
1500
  return session.onLogin(...args);
754
1501
  };
1502
+ /**
1503
+ * Register a callback function to be called when a user logs out:
1504
+ *
1505
+ * @param callback The function called when a user completes logout.
1506
+ * @since 1.3.0
1507
+ * @deprecated Prefer events.on(EVENTS.LOGOUT, callback)
1508
+ *
1509
+ */
755
1510
  const onLogout = (...args) => {
756
1511
  const session = getDefaultSession();
757
1512
  return session.onLogout(...args);
758
1513
  };
1514
+ /**
1515
+ * Register a callback function to be called when a session is restored:
1516
+ *
1517
+ * @param callback The function called when a session is restored.
1518
+ * @since 1.3.0
1519
+ * @deprecated Prefer events.on(EVENTS.SESSION_RESTORED, callback)
1520
+ */
759
1521
  const onSessionRestore = (...args) => {
760
1522
  const session = getDefaultSession();
761
1523
  return session.onSessionRestore(...args);
762
1524
  };
1525
+ /**
1526
+ * {@link SessionEventEmitter} instance to subscribe to events by the default session.
1527
+ *
1528
+ * @since 1.14.0
1529
+ */
763
1530
  const events = () => {
764
1531
  return getDefaultSession().events;
765
1532
  };