oidc-spa 8.2.1 → 8.2.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 (51) hide show
  1. package/core/AuthResponse.d.ts +0 -5
  2. package/core/AuthResponse.js +0 -25
  3. package/core/AuthResponse.js.map +1 -1
  4. package/core/createOidc.d.ts +3 -2
  5. package/core/createOidc.js +81 -117
  6. package/core/createOidc.js.map +1 -1
  7. package/core/instancesThatCantUseIframes.d.ts +2 -0
  8. package/core/instancesThatCantUseIframes.js +20 -0
  9. package/core/instancesThatCantUseIframes.js.map +1 -0
  10. package/core/loginOrGoToAuthServer.d.ts +1 -0
  11. package/core/loginOrGoToAuthServer.js +3 -0
  12. package/core/loginOrGoToAuthServer.js.map +1 -1
  13. package/core/persistedAuthState.d.ts +1 -0
  14. package/core/persistedAuthState.js +14 -4
  15. package/core/persistedAuthState.js.map +1 -1
  16. package/esm/core/AuthResponse.d.ts +0 -5
  17. package/esm/core/AuthResponse.js +0 -23
  18. package/esm/core/AuthResponse.js.map +1 -1
  19. package/esm/core/createOidc.d.ts +3 -2
  20. package/esm/core/createOidc.js +82 -118
  21. package/esm/core/createOidc.js.map +1 -1
  22. package/esm/core/instancesThatCantUseIframes.d.ts +2 -0
  23. package/esm/core/instancesThatCantUseIframes.js +16 -0
  24. package/esm/core/instancesThatCantUseIframes.js.map +1 -0
  25. package/esm/core/loginOrGoToAuthServer.d.ts +1 -0
  26. package/esm/core/loginOrGoToAuthServer.js +3 -0
  27. package/esm/core/loginOrGoToAuthServer.js.map +1 -1
  28. package/esm/core/persistedAuthState.d.ts +1 -0
  29. package/esm/core/persistedAuthState.js +14 -4
  30. package/esm/core/persistedAuthState.js.map +1 -1
  31. package/esm/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +2 -4
  32. package/esm/tools/lazySessionStorage.js +81 -0
  33. package/esm/tools/lazySessionStorage.js.map +1 -0
  34. package/package.json +1 -1
  35. package/src/core/AuthResponse.ts +0 -36
  36. package/src/core/createOidc.ts +95 -154
  37. package/src/core/instancesThatCantUseIframes.ts +24 -0
  38. package/src/core/loginOrGoToAuthServer.ts +5 -0
  39. package/src/core/persistedAuthState.ts +27 -5
  40. package/src/tools/lazySessionStorage.ts +119 -0
  41. package/src/vite-plugin/manageOptimizedDeps.ts +2 -0
  42. package/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +2 -4
  43. package/tools/lazySessionStorage.js +84 -0
  44. package/tools/lazySessionStorage.js.map +1 -0
  45. package/vite-plugin/manageOptimizedDeps.js +1 -0
  46. package/vite-plugin/manageOptimizedDeps.js.map +1 -1
  47. package/esm/tools/EphemeralSessionStorage.js +0 -143
  48. package/esm/tools/EphemeralSessionStorage.js.map +0 -1
  49. package/src/tools/EphemeralSessionStorage.ts +0 -225
  50. package/tools/EphemeralSessionStorage.js +0 -146
  51. package/tools/EphemeralSessionStorage.js.map +0 -1
@@ -25,12 +25,7 @@ import { notifyOtherTabsOfLogin, getPrOtherTabLogin } from "./loginPropagationTo
25
25
  import { getConfigId } from "./configId";
26
26
  import { oidcClientTsUserToTokens } from "./oidcClientTsUserToTokens";
27
27
  import { loginSilent } from "./loginSilent";
28
- import {
29
- authResponseToUrl,
30
- getPersistedRedirectAuthResponses,
31
- setPersistedRedirectAuthResponses,
32
- type AuthResponse
33
- } from "./AuthResponse";
28
+ import { authResponseToUrl, type AuthResponse } from "./AuthResponse";
34
29
  import { getRootRelativeOriginalLocationHref, getRedirectAuthResponse } from "./earlyInit";
35
30
  import { getPersistedAuthState, persistAuthState } from "./persistedAuthState";
36
31
  import type { Oidc } from "./Oidc";
@@ -40,7 +35,7 @@ import {
40
35
  createLoginOrGoToAuthServer,
41
36
  getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError
42
37
  } from "./loginOrGoToAuthServer";
43
- import { createEphemeralSessionStorage } from "../tools/EphemeralSessionStorage";
38
+ import { createLazySessionStorage } from "../tools/lazySessionStorage";
44
39
  import {
45
40
  startLoginOrRefreshProcess,
46
41
  waitForAllOtherOngoingLoginOrRefreshProcessesToComplete
@@ -53,6 +48,10 @@ import { prShouldLoadApp } from "./prShouldLoadApp";
53
48
  import { getBASE_URL } from "./BASE_URL";
54
49
  import { getIsLikelyDevServer } from "../tools/isLikelyDevServer";
55
50
  import { createObjectThatThrowsIfAccessed } from "../tools/createObjectThatThrowsIfAccessed";
51
+ import {
52
+ evtIsThereMoreThanOneInstanceThatCantUserIframes,
53
+ notifyNewInstanceThatCantUseIframes
54
+ } from "./instancesThatCantUseIframes";
56
55
 
57
56
  // NOTE: Replaced at build time
58
57
  const VERSION = "{{OIDC_SPA_VERSION}}";
@@ -108,6 +107,8 @@ export type ParamsOfCreateOidc<
108
107
  */
109
108
  extraTokenParams?: Record<string, string | undefined> | (() => Record<string, string | undefined>);
110
109
  /**
110
+ * @deprecated: Use login({ redirectUrl: "..." }) instead.
111
+ *
111
112
  * Usage discouraged, it's here because we don't want to assume too much on your
112
113
  * usecase but I can't think of a scenario where you would want anything
113
114
  * other than the current page.
@@ -208,24 +209,9 @@ export type ParamsOfCreateOidc<
208
209
 
209
210
  const globalContext = {
210
211
  prOidcByConfigId: new Map<string, Promise<Oidc<any>>>(),
211
- hasLogoutBeenCalled: id<boolean>(false),
212
- evtRequestToPersistTokens: createEvt<{ configIdOfInstancePostingTheRequest: string }>()
212
+ hasLogoutBeenCalled: id<boolean>(false)
213
213
  };
214
214
 
215
- globalContext.evtRequestToPersistTokens.subscribe(() => {
216
- const { authResponse } = getRedirectAuthResponse();
217
-
218
- if (authResponse === undefined) {
219
- return;
220
- }
221
-
222
- const { authResponses } = getPersistedRedirectAuthResponses();
223
-
224
- setPersistedRedirectAuthResponses({
225
- authResponses: [...authResponses, authResponse]
226
- });
227
- });
228
-
229
215
  /** @see: https://docs.oidc-spa.dev/v/v8/usage */
230
216
  export async function createOidc<
231
217
  DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.DecodedIdToken_OidcCoreSpec,
@@ -242,7 +228,7 @@ export async function createOidc<
242
228
  }
243
229
  }
244
230
 
245
- const { issuerUri: issuerUri_params, clientId, scopes = ["profile"], debugLogs, ...rest } = params;
231
+ const { issuerUri: issuerUri_params, clientId, debugLogs, ...rest } = params;
246
232
 
247
233
  const issuerUri = toFullyQualifiedUrl({
248
234
  urlish: issuerUri_params,
@@ -298,7 +284,6 @@ export async function createOidc<
298
284
  const oidc = await createOidc_nonMemoized(rest, {
299
285
  issuerUri,
300
286
  clientId,
301
- scopes,
302
287
  configId,
303
288
  log
304
289
  });
@@ -312,14 +297,10 @@ export async function createOidc_nonMemoized<
312
297
  DecodedIdToken extends Record<string, unknown>,
313
298
  AutoLogin extends boolean
314
299
  >(
315
- params: Omit<
316
- ParamsOfCreateOidc<DecodedIdToken, AutoLogin>,
317
- "issuerUri" | "clientId" | "scopes" | "debugLogs"
318
- >,
300
+ params: Omit<ParamsOfCreateOidc<DecodedIdToken, AutoLogin>, "issuerUri" | "clientId" | "debugLogs">,
319
301
  preProcessedParams: {
320
302
  issuerUri: string;
321
303
  clientId: string;
322
- scopes: string[];
323
304
  configId: string;
324
305
  log: typeof console.log | undefined;
325
306
  }
@@ -357,12 +338,13 @@ export async function createOidc_nonMemoized<
357
338
  __unsafe_clientSecret,
358
339
  __unsafe_useIdTokenAsAccessToken = false,
359
340
  __metadata,
360
- noIframe = false
341
+ noIframe = false,
342
+ scopes = ["openid", "profile"]
361
343
  } = params;
362
344
 
363
345
  const BASE_URL_params = params.BASE_URL ?? params.homeUrl;
364
346
 
365
- const { issuerUri, clientId, scopes, configId, log } = preProcessedParams;
347
+ const { issuerUri, clientId, configId, log } = preProcessedParams;
366
348
 
367
349
  const getExtraQueryParams = (() => {
368
350
  if (extraQueryParamsOrGetter === undefined) {
@@ -422,8 +404,7 @@ export async function createOidc_nonMemoized<
422
404
  issuerUri,
423
405
  clientId,
424
406
  scopes,
425
- configId,
426
- homeUrlAndRedirectUri
407
+ oidcRedirectUri: homeUrlAndRedirectUri
427
408
  },
428
409
  null,
429
410
  2
@@ -515,7 +496,7 @@ export async function createOidc_nonMemoized<
515
496
  log?.(
516
497
  [
517
498
  "Detected localhost environment.",
518
- "\nWhen reloading while logged in, you may briefly see",
499
+ "\nWhen reloading while logged in, you will briefly see",
519
500
  "some URL params appear in the address bar.",
520
501
  "\nThis happens because session restore via iframe is disabled,",
521
502
  "the browser treats your auth server as a third party.",
@@ -585,7 +566,16 @@ export async function createOidc_nonMemoized<
585
566
  return true;
586
567
  })();
587
568
 
588
- let isUserStoreInMemoryOnly: boolean | undefined = undefined;
569
+ notifyNewInstanceThatCantUseIframes();
570
+
571
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
572
+ log?.(
573
+ [
574
+ "More than one oidc instance can't use iframe",
575
+ "falling back to persisting tokens in session storage"
576
+ ].join(" ")
577
+ );
578
+ }
589
579
 
590
580
  const oidcClientTsUserManager =
591
581
  oidcMetadata === undefined
@@ -606,27 +596,18 @@ export async function createOidc_nonMemoized<
606
596
  userStore: new WebStorageStateStore({
607
597
  store: (() => {
608
598
  if (canUseIframe) {
609
- isUserStoreInMemoryOnly = true;
610
599
  return new InMemoryWebStorage();
611
600
  }
612
601
 
613
- isUserStoreInMemoryOnly = false;
614
-
615
- const storage = createEphemeralSessionStorage({
616
- sessionStorageTtlMs: 3 * 60_000
617
- });
618
-
619
- const { evtRequestToPersistTokens } = globalContext;
620
-
621
- evtRequestToPersistTokens.subscribe(
622
- ({ configIdOfInstancePostingTheRequest }) => {
623
- if (configIdOfInstancePostingTheRequest === configId) {
624
- return;
625
- }
602
+ const storage = createLazySessionStorage();
626
603
 
604
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
605
+ storage.persistCurrentStateAndSubsequentChanges();
606
+ } else {
607
+ evtIsThereMoreThanOneInstanceThatCantUserIframes.subscribe(() => {
627
608
  storage.persistCurrentStateAndSubsequentChanges();
628
- }
629
- );
609
+ });
610
+ }
630
611
 
631
612
  return storage;
632
613
  })()
@@ -675,75 +656,67 @@ export async function createOidc_nonMemoized<
675
656
  });
676
657
  }
677
658
 
678
- handle_redirect_auth_response: {
679
- let stateDataAndAuthResponse:
680
- | { stateData: StateData.Redirect; authResponse: AuthResponse }
681
- | undefined = undefined;
659
+ restore_from_session_storage: {
660
+ if (canUseIframe) {
661
+ break restore_from_session_storage;
662
+ }
682
663
 
683
- get_stateData_and_authResponse: {
684
- from_memory: {
685
- const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
664
+ if (!evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
665
+ break restore_from_session_storage;
666
+ }
686
667
 
687
- if (authResponse === undefined) {
688
- break from_memory;
689
- }
668
+ let oidcClientTsUser: OidcClientTsUser | null;
690
669
 
691
- const stateData = getStateData({ stateUrlParamValue: authResponse.state });
670
+ try {
671
+ oidcClientTsUser = await oidcClientTsUserManager.getUser();
672
+ } catch {
673
+ // NOTE: Not sure if it can throw, but let's be safe.
674
+ oidcClientTsUser = null;
675
+ try {
676
+ await oidcClientTsUserManager.removeUser();
677
+ } catch {}
678
+ }
692
679
 
693
- if (stateData === undefined) {
694
- clearAuthResponse();
695
- break from_memory;
696
- }
680
+ if (oidcClientTsUser === null) {
681
+ break restore_from_session_storage;
682
+ }
697
683
 
698
- if (stateData.configId !== configId) {
699
- break from_memory;
700
- }
684
+ log?.("Session was restored from session storage");
701
685
 
702
- assert(stateData.context === "redirect", "3229492");
686
+ return {
687
+ oidcClientTsUser,
688
+ backFromAuthServer: undefined
689
+ };
690
+ }
703
691
 
704
- clearAuthResponse();
692
+ handle_redirect_auth_response: {
693
+ let stateDataAndAuthResponse:
694
+ | { stateData: StateData.Redirect; authResponse: AuthResponse }
695
+ | undefined = undefined;
705
696
 
706
- stateDataAndAuthResponse = { stateData, authResponse };
697
+ {
698
+ const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
707
699
 
708
- break get_stateData_and_authResponse;
700
+ if (authResponse === undefined) {
701
+ break handle_redirect_auth_response;
709
702
  }
710
703
 
711
- // from storage, this is for race condition in multiple instance
712
- // setup where one instance would need to redirect before
713
- // the authResponse in memory had the chance to be processed.
714
- // This can only happen if:
715
- // 1) There are multiple oidc instances in the App.
716
- // 2) They are instantiated in a non deterministic order.
717
- // 3) We can't use iframe
718
- // We practically never persist the auth response and do it only in session
719
- // an ephemeral session storage, when we know it's gonna be required.
720
- {
721
- const { authResponses } = getPersistedRedirectAuthResponses();
722
-
723
- for (const authResponse of authResponses) {
724
- const stateData = getStateData({ stateUrlParamValue: authResponse.state });
725
-
726
- if (stateData === undefined) {
727
- continue;
728
- }
704
+ const stateData = getStateData({ stateUrlParamValue: authResponse.state });
729
705
 
730
- if (stateData.configId !== configId) {
731
- continue;
732
- }
706
+ if (stateData === undefined) {
707
+ clearAuthResponse();
708
+ break handle_redirect_auth_response;
709
+ }
733
710
 
734
- assert(stateData.context === "redirect", "35935591");
711
+ if (stateData.configId !== configId) {
712
+ break handle_redirect_auth_response;
713
+ }
735
714
 
736
- setPersistedRedirectAuthResponses({
737
- authResponses: authResponses.filter(
738
- authResponse_i => authResponse_i !== authResponse
739
- )
740
- });
715
+ assert(stateData.context === "redirect", "3229492");
741
716
 
742
- stateDataAndAuthResponse = { stateData, authResponse };
717
+ clearAuthResponse();
743
718
 
744
- break get_stateData_and_authResponse;
745
- }
746
- }
719
+ stateDataAndAuthResponse = { stateData, authResponse };
747
720
  }
748
721
 
749
722
  if (stateDataAndAuthResponse === undefined) {
@@ -856,39 +829,6 @@ export async function createOidc_nonMemoized<
856
829
  }
857
830
  }
858
831
 
859
- // NOTE: We almost never persist tokens, we have to only to support edge case
860
- // of multiple oidc instance in a single App with no iframe support.
861
- restore_from_session_storage: {
862
- assert(isUserStoreInMemoryOnly !== undefined, "3392204");
863
-
864
- if (isUserStoreInMemoryOnly) {
865
- break restore_from_session_storage;
866
- }
867
-
868
- let oidcClientTsUser: OidcClientTsUser | null;
869
-
870
- try {
871
- oidcClientTsUser = await oidcClientTsUserManager.getUser();
872
- } catch {
873
- // NOTE: Not sure if it can throw, but let's be safe.
874
- oidcClientTsUser = null;
875
- try {
876
- await oidcClientTsUserManager.removeUser();
877
- } catch {}
878
- }
879
-
880
- if (oidcClientTsUser === null) {
881
- break restore_from_session_storage;
882
- }
883
-
884
- log?.("Restored the auth from ephemeral session storage");
885
-
886
- return {
887
- oidcClientTsUser,
888
- backFromAuthServer: undefined
889
- };
890
- }
891
-
892
832
  silent_login_if_possible_and_auto_login: {
893
833
  const persistedAuthState = getPersistedAuthState({ configId });
894
834
 
@@ -1002,8 +942,6 @@ export async function createOidc_nonMemoized<
1002
942
  ) {
1003
943
  log?.("Performing auto login with redirect");
1004
944
 
1005
- persistAuthState({ configId, state: undefined });
1006
-
1007
945
  completeLoginOrRefreshProcess();
1008
946
 
1009
947
  if (autoLogin && persistedAuthState !== "logged in") {
@@ -1015,16 +953,16 @@ export async function createOidc_nonMemoized<
1015
953
  getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError()
1016
954
  });
1017
955
 
1018
- if (persistedAuthState === "logged in") {
1019
- globalContext.evtRequestToPersistTokens.post({
1020
- configIdOfInstancePostingTheRequest: configId
1021
- });
1022
- }
1023
-
1024
956
  await loginOrGoToAuthServer({
1025
957
  action: "login",
1026
958
  doForceReloadOnBfCache: true,
1027
- redirectUrl: getRootRelativeOriginalLocationHref(),
959
+ redirectUrl: (() => {
960
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
961
+ return window.location.href;
962
+ }
963
+
964
+ return getRootRelativeOriginalLocationHref();
965
+ })(),
1028
966
  // NOTE: Wether or not it's the preferred behavior, pushing to history
1029
967
  // only works on user interaction so it have to be false
1030
968
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
@@ -1040,7 +978,10 @@ export async function createOidc_nonMemoized<
1040
978
  }
1041
979
 
1042
980
  return "ensure no interaction";
1043
- })()
981
+ })(),
982
+ preRedirectHook: () => {
983
+ persistAuthState({ configId, state: undefined });
984
+ }
1044
985
  });
1045
986
  }
1046
987
 
@@ -1161,7 +1102,8 @@ export async function createOidc_nonMemoized<
1161
1102
  interaction:
1162
1103
  getPersistedAuthState({ configId }) === "explicitly logged out"
1163
1104
  ? "ensure interaction"
1164
- : "directly redirect if active session show login otherwise"
1105
+ : "directly redirect if active session show login otherwise",
1106
+ preRedirectHook: undefined
1165
1107
  });
1166
1108
  },
1167
1109
  initializationError: undefined
@@ -1233,6 +1175,7 @@ export async function createOidc_nonMemoized<
1233
1175
  state: {
1234
1176
  stateDescription: "logged in",
1235
1177
  refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
1178
+ serverDateNow: currentTokens.getServerDateNow(),
1236
1179
  idleSessionLifetimeInSeconds
1237
1180
  }
1238
1181
  });
@@ -1382,10 +1325,6 @@ export async function createOidc_nonMemoized<
1382
1325
  prUnlock: new Promise<never>(() => {})
1383
1326
  });
1384
1327
 
1385
- globalContext.evtRequestToPersistTokens.post({
1386
- configIdOfInstancePostingTheRequest: configId
1387
- });
1388
-
1389
1328
  await loginOrGoToAuthServer({
1390
1329
  action: "login",
1391
1330
  redirectUrl: window.location.href,
@@ -1393,7 +1332,8 @@ export async function createOidc_nonMemoized<
1393
1332
  extraQueryParams_local: undefined,
1394
1333
  transformUrlBeforeRedirect_local: undefined,
1395
1334
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
1396
- interaction: "directly redirect if active session show login otherwise"
1335
+ interaction: "directly redirect if active session show login otherwise",
1336
+ preRedirectHook: undefined
1397
1337
  });
1398
1338
  assert(false, "136134");
1399
1339
  };
@@ -1514,6 +1454,7 @@ export async function createOidc_nonMemoized<
1514
1454
  state: {
1515
1455
  stateDescription: "logged in",
1516
1456
  refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
1457
+ serverDateNow: currentTokens.getServerDateNow(),
1517
1458
  idleSessionLifetimeInSeconds
1518
1459
  }
1519
1460
  });
@@ -0,0 +1,24 @@
1
+ import { createStatefulEvt } from "../tools/StatefulEvt";
2
+
3
+ const SESSION_STORAGE_KEY = "oidc-spa:more-than-one-instance-cant-use-iframe";
4
+
5
+ export const evtIsThereMoreThanOneInstanceThatCantUserIframes = createStatefulEvt(
6
+ () => sessionStorage.getItem(SESSION_STORAGE_KEY) !== null
7
+ );
8
+
9
+ let count = 0;
10
+
11
+ export function notifyNewInstanceThatCantUseIframes() {
12
+ count++;
13
+
14
+ if (count === 1) {
15
+ return;
16
+ }
17
+
18
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
19
+ return;
20
+ }
21
+
22
+ sessionStorage.setItem(SESSION_STORAGE_KEY, "true");
23
+ evtIsThereMoreThanOneInstanceThatCantUserIframes.current = true;
24
+ }
@@ -30,6 +30,7 @@ namespace Params {
30
30
  | "ensure no interaction"
31
31
  | "ensure interaction"
32
32
  | "directly redirect if active session show login otherwise";
33
+ preRedirectHook: (() => void) | undefined;
33
34
  };
34
35
 
35
36
  export type GoToAuthServer = Common & {
@@ -305,6 +306,10 @@ export function createLoginOrGoToAuthServer(params: {
305
306
 
306
307
  log?.(`redirectMethod: ${redirectMethod}`);
307
308
 
309
+ if (rest.action === "login") {
310
+ rest.preRedirectHook?.();
311
+ }
312
+
308
313
  return oidcClientTsUserManager
309
314
  .signinRedirect({
310
315
  state: stateData,
@@ -30,6 +30,7 @@ export function persistAuthState(params: {
30
30
  stateDescription: "logged in";
31
31
  idleSessionLifetimeInSeconds: number | undefined;
32
32
  refreshTokenExpirationTime: number | undefined;
33
+ serverDateNow: number;
33
34
  }
34
35
  | {
35
36
  stateDescription: "explicitly logged out";
@@ -56,14 +57,35 @@ export function persistAuthState(params: {
56
57
  __brand: "PersistedAuthState-v1",
57
58
  stateDescription: "logged in",
58
59
  untilTime: (() => {
59
- const { idleSessionLifetimeInSeconds, refreshTokenExpirationTime } =
60
- state;
60
+ const {
61
+ idleSessionLifetimeInSeconds,
62
+ refreshTokenExpirationTime,
63
+ serverDateNow
64
+ } = state;
65
+
66
+ const untilTime_real = (() => {
67
+ if (refreshTokenExpirationTime === undefined) {
68
+ return undefined;
69
+ }
70
+
71
+ const msBeforeExpirationOfTheSession =
72
+ refreshTokenExpirationTime - serverDateNow;
73
+
74
+ return Date.now() + msBeforeExpirationOfTheSession;
75
+ })();
76
+
77
+ const unitTime_userOverwrite = (() => {
78
+ if (idleSessionLifetimeInSeconds === undefined) {
79
+ return undefined;
80
+ }
61
81
 
62
- if (idleSessionLifetimeInSeconds !== undefined) {
63
82
  return Date.now() + idleSessionLifetimeInSeconds * 1000;
64
- }
83
+ })();
65
84
 
66
- return refreshTokenExpirationTime;
85
+ return Math.min(
86
+ untilTime_real ?? Number.POSITIVE_INFINITY,
87
+ unitTime_userOverwrite ?? Number.POSITIVE_INFINITY
88
+ );
67
89
  })()
68
90
  });
69
91
  case "explicitly logged out":
@@ -0,0 +1,119 @@
1
+ import { assert } from "../tools/tsafe/assert";
2
+
3
+ const SESSION_STORAGE_PREFIX = "lazy-session-storage:";
4
+
5
+ export type LazySessionStorage = {
6
+ // `Storage` methods, we don't use the type directly because it has [name: string]: any;
7
+ readonly length: number;
8
+ clear(): void;
9
+ getItem(key: string): string | null;
10
+ key(index: number): string | null;
11
+ removeItem(key: string): void;
12
+ setItem(key: string, value: string): void;
13
+
14
+ // Custom method
15
+ persistCurrentStateAndSubsequentChanges: () => void;
16
+ };
17
+
18
+ export function createLazySessionStorage(): LazySessionStorage {
19
+ const entries: { key: string; value: string }[] = [];
20
+
21
+ for (let i = 0; i < sessionStorage.length; i++) {
22
+ const key = sessionStorage.key(i);
23
+ assert(key !== null, "470498");
24
+
25
+ if (!key.startsWith(SESSION_STORAGE_PREFIX)) {
26
+ continue;
27
+ }
28
+
29
+ const value = sessionStorage.getItem(key);
30
+
31
+ assert(value !== null, "846771");
32
+
33
+ sessionStorage.removeItem(key);
34
+
35
+ entries.push({
36
+ key: key.slice(SESSION_STORAGE_PREFIX.length),
37
+ value
38
+ });
39
+ }
40
+
41
+ let isPersistenceEnabled = false;
42
+
43
+ const storage: LazySessionStorage = {
44
+ persistCurrentStateAndSubsequentChanges: () => {
45
+ isPersistenceEnabled = true;
46
+
47
+ for (let i = 0; i < storage.length; i++) {
48
+ const key = storage.key(i);
49
+ assert(key !== null, "803385");
50
+
51
+ const value = storage.getItem(key);
52
+
53
+ assert(value !== null, "777098");
54
+
55
+ storage.setItem(key, value);
56
+ }
57
+ },
58
+ get length() {
59
+ return entries.length;
60
+ },
61
+ key: index => {
62
+ const entry = entries[index];
63
+
64
+ if (entry === undefined) {
65
+ return null;
66
+ }
67
+
68
+ return entry.key;
69
+ },
70
+ removeItem: key => {
71
+ const entry = entries.find(entry => entry.key === key);
72
+
73
+ if (entry === undefined) {
74
+ return;
75
+ }
76
+
77
+ sessionStorage.removeItem(`${SESSION_STORAGE_PREFIX}${entry.key}`);
78
+
79
+ const index = entries.indexOf(entry);
80
+
81
+ entries.splice(index, 1);
82
+ },
83
+ clear: () => {
84
+ for (let i = 0; i < storage.length; i++) {
85
+ const key = storage.key(i);
86
+ assert(key !== null, "290875");
87
+ storage.removeItem(key);
88
+ }
89
+ },
90
+ getItem: key => {
91
+ const entry = entries.find(entry => entry.key === key);
92
+ if (entry === undefined) {
93
+ return null;
94
+ }
95
+ return entry.value;
96
+ },
97
+ setItem: (key, value) => {
98
+ if (isPersistenceEnabled) {
99
+ sessionStorage.setItem(`${SESSION_STORAGE_PREFIX}${key}`, value);
100
+ }
101
+
102
+ update: {
103
+ const entry = entries.find(entry => entry.key === key);
104
+
105
+ if (entry === undefined) {
106
+ break update;
107
+ }
108
+
109
+ entry.value = value;
110
+
111
+ return;
112
+ }
113
+
114
+ entries.push({ key, value });
115
+ }
116
+ };
117
+
118
+ return storage;
119
+ }
@@ -55,6 +55,8 @@ export function manageOptimizedDeps(params: {
55
55
  moduleNames_include.push("zod");
56
56
  }
57
57
 
58
+ moduleNames_include.push("oidc-spa");
59
+
58
60
  ((userConfig.optimizeDeps ??= {}).include ??= []).push(...moduleNames_include);
59
61
  }
60
62
  break;
@@ -1,4 +1,4 @@
1
- export type EphemeralSessionStorage = {
1
+ export type LazySessionStorage = {
2
2
  readonly length: number;
3
3
  clear(): void;
4
4
  getItem(key: string): string | null;
@@ -7,6 +7,4 @@ export type EphemeralSessionStorage = {
7
7
  setItem(key: string, value: string): void;
8
8
  persistCurrentStateAndSubsequentChanges: () => void;
9
9
  };
10
- export declare function createEphemeralSessionStorage(params: {
11
- sessionStorageTtlMs: number;
12
- }): EphemeralSessionStorage;
10
+ export declare function createLazySessionStorage(): LazySessionStorage;