oidc-spa 8.2.1 → 8.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) 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 +49 -16
  5. package/core/createOidc.js +97 -122
  6. package/core/createOidc.js.map +1 -1
  7. package/core/desiredPostLoginRedirectUrl.d.ts +4 -0
  8. package/core/desiredPostLoginRedirectUrl.js +12 -0
  9. package/core/desiredPostLoginRedirectUrl.js.map +1 -0
  10. package/core/diagnostic.d.ts +1 -1
  11. package/core/diagnostic.js +3 -3
  12. package/core/diagnostic.js.map +1 -1
  13. package/core/instancesThatCantUseIframes.d.ts +2 -0
  14. package/core/instancesThatCantUseIframes.js +20 -0
  15. package/core/instancesThatCantUseIframes.js.map +1 -0
  16. package/core/loginOrGoToAuthServer.d.ts +1 -0
  17. package/core/loginOrGoToAuthServer.js +3 -0
  18. package/core/loginOrGoToAuthServer.js.map +1 -1
  19. package/core/persistedAuthState.d.ts +1 -0
  20. package/core/persistedAuthState.js +14 -4
  21. package/core/persistedAuthState.js.map +1 -1
  22. package/esm/angular.d.ts +27 -4
  23. package/esm/angular.js +28 -6
  24. package/esm/angular.js.map +1 -1
  25. package/esm/core/AuthResponse.d.ts +0 -5
  26. package/esm/core/AuthResponse.js +0 -23
  27. package/esm/core/AuthResponse.js.map +1 -1
  28. package/esm/core/createOidc.d.ts +49 -16
  29. package/esm/core/createOidc.js +98 -123
  30. package/esm/core/createOidc.js.map +1 -1
  31. package/esm/core/desiredPostLoginRedirectUrl.d.ts +4 -0
  32. package/esm/core/desiredPostLoginRedirectUrl.js +8 -0
  33. package/esm/core/desiredPostLoginRedirectUrl.js.map +1 -0
  34. package/esm/core/diagnostic.d.ts +1 -1
  35. package/esm/core/diagnostic.js +3 -3
  36. package/esm/core/diagnostic.js.map +1 -1
  37. package/esm/core/instancesThatCantUseIframes.d.ts +2 -0
  38. package/esm/core/instancesThatCantUseIframes.js +16 -0
  39. package/esm/core/instancesThatCantUseIframes.js.map +1 -0
  40. package/esm/core/loginOrGoToAuthServer.d.ts +1 -0
  41. package/esm/core/loginOrGoToAuthServer.js +3 -0
  42. package/esm/core/loginOrGoToAuthServer.js.map +1 -1
  43. package/esm/core/persistedAuthState.d.ts +1 -0
  44. package/esm/core/persistedAuthState.js +14 -4
  45. package/esm/core/persistedAuthState.js.map +1 -1
  46. package/esm/keycloak/keycloak-js/Keycloak.d.ts +40 -0
  47. package/esm/keycloak/keycloak-js/Keycloak.js +2 -1
  48. package/esm/keycloak/keycloak-js/Keycloak.js.map +1 -1
  49. package/esm/react/react.js +24 -2
  50. package/esm/react/react.js.map +1 -1
  51. package/esm/react-spa/createOidcSpaApi.js +26 -4
  52. package/esm/react-spa/createOidcSpaApi.js.map +1 -1
  53. package/esm/react-spa/types.d.ts +26 -3
  54. package/esm/tanstack-start/react/createOidcSpaApi.js +25 -3
  55. package/esm/tanstack-start/react/createOidcSpaApi.js.map +1 -1
  56. package/esm/tanstack-start/react/types.d.ts +26 -3
  57. package/esm/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +4 -4
  58. package/esm/tools/lazySessionStorage.js +83 -0
  59. package/esm/tools/lazySessionStorage.js.map +1 -0
  60. package/keycloak/keycloak-js/Keycloak.d.ts +40 -0
  61. package/keycloak/keycloak-js/Keycloak.js +2 -1
  62. package/keycloak/keycloak-js/Keycloak.js.map +1 -1
  63. package/package.json +5 -1
  64. package/react/react.js +24 -2
  65. package/react/react.js.map +1 -1
  66. package/react-spa/createOidcSpaApi.js +26 -4
  67. package/react-spa/createOidcSpaApi.js.map +1 -1
  68. package/react-spa/types.d.ts +26 -3
  69. package/src/angular.ts +72 -18
  70. package/src/core/AuthResponse.ts +0 -36
  71. package/src/core/createOidc.ts +160 -173
  72. package/src/core/desiredPostLoginRedirectUrl.ts +9 -0
  73. package/src/core/diagnostic.ts +4 -4
  74. package/src/core/instancesThatCantUseIframes.ts +24 -0
  75. package/src/core/loginOrGoToAuthServer.ts +5 -0
  76. package/src/core/persistedAuthState.ts +27 -5
  77. package/src/keycloak/keycloak-js/Keycloak.ts +43 -1
  78. package/src/react/react.tsx +32 -3
  79. package/src/react-spa/createOidcSpaApi.tsx +34 -5
  80. package/src/react-spa/types.tsx +26 -3
  81. package/src/tanstack-start/react/createOidcSpaApi.tsx +33 -4
  82. package/src/tanstack-start/react/types.tsx +26 -3
  83. package/src/tools/lazySessionStorage.ts +123 -0
  84. package/src/vite-plugin/manageOptimizedDeps.ts +4 -1
  85. package/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +4 -4
  86. package/tools/lazySessionStorage.js +86 -0
  87. package/tools/lazySessionStorage.js.map +1 -0
  88. package/vite-plugin/manageOptimizedDeps.js +3 -1
  89. package/vite-plugin/manageOptimizedDeps.js.map +1 -1
  90. package/esm/tools/EphemeralSessionStorage.js +0 -143
  91. package/esm/tools/EphemeralSessionStorage.js.map +0 -1
  92. package/src/tools/EphemeralSessionStorage.ts +0 -225
  93. package/tools/EphemeralSessionStorage.js +0 -146
  94. 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,11 @@ 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";
55
+ import { getDesiredPostLoginRedirectUrl } from "./desiredPostLoginRedirectUrl";
56
56
 
57
57
  // NOTE: Replaced at build time
58
58
  const VERSION = "{{OIDC_SPA_VERSION}}";
@@ -107,19 +107,6 @@ export type ParamsOfCreateOidc<
107
107
  * extraTokenParams: { selectedCustomer: "xxx" }
108
108
  */
109
109
  extraTokenParams?: Record<string, string | undefined> | (() => Record<string, string | undefined>);
110
- /**
111
- * Usage discouraged, it's here because we don't want to assume too much on your
112
- * usecase but I can't think of a scenario where you would want anything
113
- * other than the current page.
114
- *
115
- * Where to redirect after successful login.
116
- * Default: window.location.href (here)
117
- *
118
- * It does not need to include the origin, eg: "/dashboard"
119
- *
120
- * This parameter can also be passed to login() directly as `redirectUrl`.
121
- */
122
- postLoginRedirectUrl?: string;
123
110
 
124
111
  decodedIdTokenSchema?: {
125
112
  parse: (decodedIdToken_original: Oidc.Tokens.DecodedIdToken_OidcCoreSpec) => DecodedIdToken;
@@ -149,9 +136,43 @@ export type ParamsOfCreateOidc<
149
136
  autoLogin?: AutoLogin;
150
137
 
151
138
  /**
139
+ * Determines how session restoration is handled.
140
+ * Session restoration allows users to stay logged in between visits
141
+ * without needing to explicitly sign in each time.
142
+ *
143
+ * Options:
144
+ *
145
+ * - **"auto" (default)**:
146
+ * Automatically selects the best method.
147
+ * If the app’s domain shares a common parent domain with the authorization endpoint,
148
+ * an iframe is used for silent session restoration.
149
+ * Otherwise, a full-page redirect is used.
150
+ *
151
+ * - **"full page redirect"**:
152
+ * Forces full-page reloads for session restoration.
153
+ * Use this if your application is served with a restrictive CSP
154
+ * (e.g., `Content-Security-Policy: frame-ancestors "none"`)
155
+ * or `X-Frame-Options: DENY`, and you cannot modify those headers.
156
+ * This mode provides a slightly less seamless UX and will lead oidc-spa to
157
+ * store tokens in `localStorage` if multiple OIDC clients are used
158
+ * (e.g., your app communicates with several APIs).
159
+ *
160
+ * - **"iframe"**:
161
+ * Forces iframe-based session restoration.
162
+ * In development, if you go in your browser setting and allow your auth server’s domain
163
+ * to set third-party cookies this value will let you test your app
164
+ * with the local dev server as it will behave in production.
165
+ *
166
+ * See: https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration
167
+ */
168
+ sessionRestorationMethod?: "iframe" | "full page redirect" | "auto";
169
+
170
+ /**
171
+ * @deprecated Use `sessionRestorationMethod: "full page redirect"` instead.
172
+ *
152
173
  * Default: false
153
174
  *
154
- * See: https://docs.oidc-spa.dev/v/v8/resources/iframe-related-issues
175
+ * See: https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration
155
176
  */
156
177
  noIframe?: boolean;
157
178
 
@@ -204,28 +225,28 @@ export type ParamsOfCreateOidc<
204
225
 
205
226
  /** @deprecated: Use BASE_URL (same thing, just renamed). */
206
227
  homeUrl?: string;
228
+
229
+ /**
230
+ * This parameter is irrelevant in most usecases.
231
+ * It tells where to redirect after a successful login or autoLogin.
232
+ *
233
+ * If you are not in autoLogin mode there is absolutely no reason to use
234
+ * this parameter since you can pass `login({ redirectUrl: "..." })`.
235
+ *
236
+ * It can only be useful in some edge case with `autoLogin: true`
237
+ * When you want to precisely redirect somewhere after login.
238
+ *
239
+ * This can make sense if you have multiple clients to talk with different
240
+ * API and no iframe capabilities.
241
+ */
242
+ postLoginRedirectUrl?: string;
207
243
  };
208
244
 
209
245
  const globalContext = {
210
246
  prOidcByConfigId: new Map<string, Promise<Oidc<any>>>(),
211
- hasLogoutBeenCalled: id<boolean>(false),
212
- evtRequestToPersistTokens: createEvt<{ configIdOfInstancePostingTheRequest: string }>()
247
+ hasLogoutBeenCalled: id<boolean>(false)
213
248
  };
214
249
 
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
250
  /** @see: https://docs.oidc-spa.dev/v/v8/usage */
230
251
  export async function createOidc<
231
252
  DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.DecodedIdToken_OidcCoreSpec,
@@ -242,7 +263,7 @@ export async function createOidc<
242
263
  }
243
264
  }
244
265
 
245
- const { issuerUri: issuerUri_params, clientId, scopes = ["profile"], debugLogs, ...rest } = params;
266
+ const { issuerUri: issuerUri_params, clientId, debugLogs, ...rest } = params;
246
267
 
247
268
  const issuerUri = toFullyQualifiedUrl({
248
269
  urlish: issuerUri_params,
@@ -298,7 +319,6 @@ export async function createOidc<
298
319
  const oidc = await createOidc_nonMemoized(rest, {
299
320
  issuerUri,
300
321
  clientId,
301
- scopes,
302
322
  configId,
303
323
  log
304
324
  });
@@ -312,14 +332,10 @@ export async function createOidc_nonMemoized<
312
332
  DecodedIdToken extends Record<string, unknown>,
313
333
  AutoLogin extends boolean
314
334
  >(
315
- params: Omit<
316
- ParamsOfCreateOidc<DecodedIdToken, AutoLogin>,
317
- "issuerUri" | "clientId" | "scopes" | "debugLogs"
318
- >,
335
+ params: Omit<ParamsOfCreateOidc<DecodedIdToken, AutoLogin>, "issuerUri" | "clientId" | "debugLogs">,
319
336
  preProcessedParams: {
320
337
  issuerUri: string;
321
338
  clientId: string;
322
- scopes: string[];
323
339
  configId: string;
324
340
  log: typeof console.log | undefined;
325
341
  }
@@ -357,12 +373,13 @@ export async function createOidc_nonMemoized<
357
373
  __unsafe_clientSecret,
358
374
  __unsafe_useIdTokenAsAccessToken = false,
359
375
  __metadata,
360
- noIframe = false
376
+ scopes = ["openid", "profile"],
377
+ sessionRestorationMethod = params.autoLogin === true ? "full page redirect" : "auto"
361
378
  } = params;
362
379
 
363
380
  const BASE_URL_params = params.BASE_URL ?? params.homeUrl;
364
381
 
365
- const { issuerUri, clientId, scopes, configId, log } = preProcessedParams;
382
+ const { issuerUri, clientId, configId, log } = preProcessedParams;
366
383
 
367
384
  const getExtraQueryParams = (() => {
368
385
  if (extraQueryParamsOrGetter === undefined) {
@@ -422,8 +439,7 @@ export async function createOidc_nonMemoized<
422
439
  issuerUri,
423
440
  clientId,
424
441
  scopes,
425
- configId,
426
- homeUrlAndRedirectUri
442
+ oidcRedirectUri: homeUrlAndRedirectUri
427
443
  },
428
444
  null,
429
445
  2
@@ -435,8 +451,15 @@ export async function createOidc_nonMemoized<
435
451
  const oidcMetadata = __metadata ?? (await fetchOidcMetadata({ issuerUri }));
436
452
 
437
453
  const canUseIframe = (() => {
438
- if (noIframe) {
439
- return false;
454
+ switch (sessionRestorationMethod) {
455
+ case "auto":
456
+ break;
457
+ case "full page redirect":
458
+ return false;
459
+ case "iframe":
460
+ return true;
461
+ default:
462
+ assert<Equals<typeof sessionRestorationMethod, never>>;
440
463
  }
441
464
 
442
465
  third_party_cookies: {
@@ -515,7 +538,7 @@ export async function createOidc_nonMemoized<
515
538
  log?.(
516
539
  [
517
540
  "Detected localhost environment.",
518
- "\nWhen reloading while logged in, you may briefly see",
541
+ "\nWhen reloading while logged in, you will briefly see",
519
542
  "some URL params appear in the address bar.",
520
543
  "\nThis happens because session restore via iframe is disabled,",
521
544
  "the browser treats your auth server as a third party.",
@@ -544,7 +567,7 @@ export async function createOidc_nonMemoized<
544
567
  ];
545
568
  })(),
546
569
  "\n\nMore info:",
547
- "https://docs.oidc-spa.dev/v/v8/resources/end-of-third-party-cookies#when-are-cookies-considered-third-party"
570
+ "https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration"
548
571
  ].join(" ")
549
572
  );
550
573
  } else {
@@ -574,7 +597,7 @@ export async function createOidc_nonMemoized<
574
597
  ];
575
598
  })(),
576
599
  "\nMore info:",
577
- "https://docs.oidc-spa.dev/v/v8/resources/end-of-third-party-cookies#when-are-cookies-considered-third-party"
600
+ "https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration"
578
601
  ].join(" ")
579
602
  );
580
603
  }
@@ -585,7 +608,16 @@ export async function createOidc_nonMemoized<
585
608
  return true;
586
609
  })();
587
610
 
588
- let isUserStoreInMemoryOnly: boolean | undefined = undefined;
611
+ notifyNewInstanceThatCantUseIframes();
612
+
613
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
614
+ log?.(
615
+ [
616
+ "More than one oidc instance can't use iframe",
617
+ "falling back to persisting tokens in session storage"
618
+ ].join(" ")
619
+ );
620
+ }
589
621
 
590
622
  const oidcClientTsUserManager =
591
623
  oidcMetadata === undefined
@@ -606,27 +638,18 @@ export async function createOidc_nonMemoized<
606
638
  userStore: new WebStorageStateStore({
607
639
  store: (() => {
608
640
  if (canUseIframe) {
609
- isUserStoreInMemoryOnly = true;
610
641
  return new InMemoryWebStorage();
611
642
  }
612
643
 
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
- }
644
+ const storage = createLazySessionStorage({ storageId: configId });
626
645
 
646
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
647
+ storage.persistCurrentStateAndSubsequentChanges();
648
+ } else {
649
+ evtIsThereMoreThanOneInstanceThatCantUserIframes.subscribe(() => {
627
650
  storage.persistCurrentStateAndSubsequentChanges();
628
- }
629
- );
651
+ });
652
+ }
630
653
 
631
654
  return storage;
632
655
  })()
@@ -675,75 +698,67 @@ export async function createOidc_nonMemoized<
675
698
  });
676
699
  }
677
700
 
678
- handle_redirect_auth_response: {
679
- let stateDataAndAuthResponse:
680
- | { stateData: StateData.Redirect; authResponse: AuthResponse }
681
- | undefined = undefined;
701
+ restore_from_session_storage: {
702
+ if (canUseIframe) {
703
+ break restore_from_session_storage;
704
+ }
682
705
 
683
- get_stateData_and_authResponse: {
684
- from_memory: {
685
- const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
706
+ if (!evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
707
+ break restore_from_session_storage;
708
+ }
686
709
 
687
- if (authResponse === undefined) {
688
- break from_memory;
689
- }
710
+ let oidcClientTsUser: OidcClientTsUser | null;
690
711
 
691
- const stateData = getStateData({ stateUrlParamValue: authResponse.state });
712
+ try {
713
+ oidcClientTsUser = await oidcClientTsUserManager.getUser();
714
+ } catch {
715
+ // NOTE: Not sure if it can throw, but let's be safe.
716
+ oidcClientTsUser = null;
717
+ try {
718
+ await oidcClientTsUserManager.removeUser();
719
+ } catch {}
720
+ }
692
721
 
693
- if (stateData === undefined) {
694
- clearAuthResponse();
695
- break from_memory;
696
- }
722
+ if (oidcClientTsUser === null) {
723
+ break restore_from_session_storage;
724
+ }
697
725
 
698
- if (stateData.configId !== configId) {
699
- break from_memory;
700
- }
726
+ log?.("Session was restored from session storage");
701
727
 
702
- assert(stateData.context === "redirect", "3229492");
728
+ return {
729
+ oidcClientTsUser,
730
+ backFromAuthServer: undefined
731
+ };
732
+ }
703
733
 
704
- clearAuthResponse();
734
+ handle_redirect_auth_response: {
735
+ let stateDataAndAuthResponse:
736
+ | { stateData: StateData.Redirect; authResponse: AuthResponse }
737
+ | undefined = undefined;
705
738
 
706
- stateDataAndAuthResponse = { stateData, authResponse };
739
+ {
740
+ const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
707
741
 
708
- break get_stateData_and_authResponse;
742
+ if (authResponse === undefined) {
743
+ break handle_redirect_auth_response;
709
744
  }
710
745
 
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
- }
746
+ const stateData = getStateData({ stateUrlParamValue: authResponse.state });
729
747
 
730
- if (stateData.configId !== configId) {
731
- continue;
732
- }
748
+ if (stateData === undefined) {
749
+ clearAuthResponse();
750
+ break handle_redirect_auth_response;
751
+ }
733
752
 
734
- assert(stateData.context === "redirect", "35935591");
753
+ if (stateData.configId !== configId) {
754
+ break handle_redirect_auth_response;
755
+ }
735
756
 
736
- setPersistedRedirectAuthResponses({
737
- authResponses: authResponses.filter(
738
- authResponse_i => authResponse_i !== authResponse
739
- )
740
- });
757
+ assert(stateData.context === "redirect", "3229492");
741
758
 
742
- stateDataAndAuthResponse = { stateData, authResponse };
759
+ clearAuthResponse();
743
760
 
744
- break get_stateData_and_authResponse;
745
- }
746
- }
761
+ stateDataAndAuthResponse = { stateData, authResponse };
747
762
  }
748
763
 
749
764
  if (stateDataAndAuthResponse === undefined) {
@@ -856,39 +871,6 @@ export async function createOidc_nonMemoized<
856
871
  }
857
872
  }
858
873
 
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
874
  silent_login_if_possible_and_auto_login: {
893
875
  const persistedAuthState = getPersistedAuthState({ configId });
894
876
 
@@ -956,7 +938,7 @@ export async function createOidc_nonMemoized<
956
938
  redirectUri: homeUrlAndRedirectUri,
957
939
  clientId,
958
940
  issuerUri,
959
- noIframe
941
+ canUseIframe
960
942
  });
961
943
  }
962
944
 
@@ -1002,8 +984,6 @@ export async function createOidc_nonMemoized<
1002
984
  ) {
1003
985
  log?.("Performing auto login with redirect");
1004
986
 
1005
- persistAuthState({ configId, state: undefined });
1006
-
1007
987
  completeLoginOrRefreshProcess();
1008
988
 
1009
989
  if (autoLogin && persistedAuthState !== "logged in") {
@@ -1015,16 +995,20 @@ export async function createOidc_nonMemoized<
1015
995
  getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError()
1016
996
  });
1017
997
 
1018
- if (persistedAuthState === "logged in") {
1019
- globalContext.evtRequestToPersistTokens.post({
1020
- configIdOfInstancePostingTheRequest: configId
1021
- });
1022
- }
1023
-
1024
998
  await loginOrGoToAuthServer({
1025
999
  action: "login",
1026
1000
  doForceReloadOnBfCache: true,
1027
- redirectUrl: getRootRelativeOriginalLocationHref(),
1001
+ redirectUrl: (() => {
1002
+ if (postLoginRedirectUrl_default) {
1003
+ return postLoginRedirectUrl_default;
1004
+ }
1005
+
1006
+ if (!evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
1007
+ return getRootRelativeOriginalLocationHref();
1008
+ }
1009
+
1010
+ return getDesiredPostLoginRedirectUrl() ?? window.location.href;
1011
+ })(),
1028
1012
  // NOTE: Wether or not it's the preferred behavior, pushing to history
1029
1013
  // only works on user interaction so it have to be false
1030
1014
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
@@ -1040,7 +1024,10 @@ export async function createOidc_nonMemoized<
1040
1024
  }
1041
1025
 
1042
1026
  return "ensure no interaction";
1043
- })()
1027
+ })(),
1028
+ preRedirectHook: () => {
1029
+ persistAuthState({ configId, state: undefined });
1030
+ }
1044
1031
  });
1045
1032
  }
1046
1033
 
@@ -1161,7 +1148,8 @@ export async function createOidc_nonMemoized<
1161
1148
  interaction:
1162
1149
  getPersistedAuthState({ configId }) === "explicitly logged out"
1163
1150
  ? "ensure interaction"
1164
- : "directly redirect if active session show login otherwise"
1151
+ : "directly redirect if active session show login otherwise",
1152
+ preRedirectHook: undefined
1165
1153
  });
1166
1154
  },
1167
1155
  initializationError: undefined
@@ -1233,6 +1221,7 @@ export async function createOidc_nonMemoized<
1233
1221
  state: {
1234
1222
  stateDescription: "logged in",
1235
1223
  refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
1224
+ serverDateNow: currentTokens.getServerDateNow(),
1236
1225
  idleSessionLifetimeInSeconds
1237
1226
  }
1238
1227
  });
@@ -1382,10 +1371,6 @@ export async function createOidc_nonMemoized<
1382
1371
  prUnlock: new Promise<never>(() => {})
1383
1372
  });
1384
1373
 
1385
- globalContext.evtRequestToPersistTokens.post({
1386
- configIdOfInstancePostingTheRequest: configId
1387
- });
1388
-
1389
1374
  await loginOrGoToAuthServer({
1390
1375
  action: "login",
1391
1376
  redirectUrl: window.location.href,
@@ -1393,7 +1378,8 @@ export async function createOidc_nonMemoized<
1393
1378
  extraQueryParams_local: undefined,
1394
1379
  transformUrlBeforeRedirect_local: undefined,
1395
1380
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
1396
- interaction: "directly redirect if active session show login otherwise"
1381
+ interaction: "directly redirect if active session show login otherwise",
1382
+ preRedirectHook: undefined
1397
1383
  });
1398
1384
  assert(false, "136134");
1399
1385
  };
@@ -1514,6 +1500,7 @@ export async function createOidc_nonMemoized<
1514
1500
  state: {
1515
1501
  stateDescription: "logged in",
1516
1502
  refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
1503
+ serverDateNow: currentTokens.getServerDateNow(),
1517
1504
  idleSessionLifetimeInSeconds
1518
1505
  }
1519
1506
  });
@@ -0,0 +1,9 @@
1
+ let postLoginRedirectUrl: string | undefined;
2
+
3
+ export function setDesiredPostLoginRedirectUrl(params: { postLoginRedirectUrl: string | undefined }) {
4
+ postLoginRedirectUrl = params.postLoginRedirectUrl;
5
+ }
6
+
7
+ export function getDesiredPostLoginRedirectUrl() {
8
+ return postLoginRedirectUrl;
9
+ }
@@ -90,9 +90,9 @@ export async function createIframeTimeoutInitializationError(params: {
90
90
  redirectUri: string;
91
91
  issuerUri: string;
92
92
  clientId: string;
93
- noIframe: boolean;
93
+ canUseIframe: boolean;
94
94
  }): Promise<OidcInitializationError> {
95
- const { redirectUri, issuerUri, clientId, noIframe } = params;
95
+ const { redirectUri, issuerUri, clientId, canUseIframe } = params;
96
96
 
97
97
  check_if_well_known_endpoint_is_reachable: {
98
98
  const isValid = await getIsValidRemoteJson(`${issuerUri}${WELL_KNOWN_PATH}`);
@@ -105,7 +105,7 @@ export async function createIframeTimeoutInitializationError(params: {
105
105
  }
106
106
 
107
107
  iframe_blocked: {
108
- if (noIframe) {
108
+ if (!canUseIframe) {
109
109
  break iframe_blocked;
110
110
  }
111
111
 
@@ -189,7 +189,7 @@ export async function createIframeTimeoutInitializationError(params: {
189
189
  messageOrCause: [
190
190
  `${redirectUri} is currently served by your web server with the HTTP header \`${key_problem}: ${headers[key_problem]}\`.\n`,
191
191
  "This header prevents the silent sign-in process from working.\n",
192
- "Refer to this documentation page to fix this issue: https://docs.oidc-spa.dev/v/v8/resources/iframe-related-issues"
192
+ "Refer to this documentation page to fix this issue: https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration"
193
193
  ].join(" ")
194
194
  });
195
195
  }
@@ -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,