oidc-spa 8.2.0 → 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 (84) 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/OidcMetadata.d.ts +5 -0
  5. package/core/OidcMetadata.js +56 -0
  6. package/core/OidcMetadata.js.map +1 -1
  7. package/core/createOidc.d.ts +4 -3
  8. package/core/createOidc.js +229 -197
  9. package/core/createOidc.js.map +1 -1
  10. package/core/diagnostic.d.ts +0 -1
  11. package/core/diagnostic.js +18 -5
  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 -1
  17. package/core/loginOrGoToAuthServer.js +4 -16
  18. package/core/loginOrGoToAuthServer.js.map +1 -1
  19. package/core/loginSilent.d.ts +1 -2
  20. package/core/loginSilent.js +3 -21
  21. package/core/loginSilent.js.map +1 -1
  22. package/core/persistedAuthState.d.ts +1 -0
  23. package/core/persistedAuthState.js +14 -4
  24. package/core/persistedAuthState.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/OidcMetadata.d.ts +5 -0
  29. package/esm/core/OidcMetadata.js +54 -0
  30. package/esm/core/OidcMetadata.js.map +1 -1
  31. package/esm/core/createOidc.d.ts +4 -3
  32. package/esm/core/createOidc.js +230 -198
  33. package/esm/core/createOidc.js.map +1 -1
  34. package/esm/core/diagnostic.d.ts +0 -1
  35. package/esm/core/diagnostic.js +15 -1
  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 -1
  41. package/esm/core/loginOrGoToAuthServer.js +4 -16
  42. package/esm/core/loginOrGoToAuthServer.js.map +1 -1
  43. package/esm/core/loginSilent.d.ts +1 -2
  44. package/esm/core/loginSilent.js +3 -21
  45. package/esm/core/loginSilent.js.map +1 -1
  46. package/esm/core/persistedAuthState.d.ts +1 -0
  47. package/esm/core/persistedAuthState.js +14 -4
  48. package/esm/core/persistedAuthState.js.map +1 -1
  49. package/esm/keycloak/keycloakIssuerUriParsed.js +8 -1
  50. package/esm/keycloak/keycloakIssuerUriParsed.js.map +1 -1
  51. package/esm/tools/isLikelyDevServer.d.ts +1 -0
  52. package/esm/tools/isLikelyDevServer.js +14 -0
  53. package/esm/tools/isLikelyDevServer.js.map +1 -0
  54. package/esm/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +2 -4
  55. package/esm/tools/lazySessionStorage.js +81 -0
  56. package/esm/tools/lazySessionStorage.js.map +1 -0
  57. package/keycloak/keycloakIssuerUriParsed.js +8 -1
  58. package/keycloak/keycloakIssuerUriParsed.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/core/AuthResponse.ts +0 -36
  61. package/src/core/OidcMetadata.ts +75 -0
  62. package/src/core/createOidc.ts +277 -264
  63. package/src/core/diagnostic.ts +21 -2
  64. package/src/core/instancesThatCantUseIframes.ts +24 -0
  65. package/src/core/loginOrGoToAuthServer.ts +5 -22
  66. package/src/core/loginSilent.ts +4 -27
  67. package/src/core/persistedAuthState.ts +27 -5
  68. package/src/keycloak/keycloakIssuerUriParsed.ts +10 -1
  69. package/src/tools/isLikelyDevServer.ts +17 -0
  70. package/src/tools/lazySessionStorage.ts +119 -0
  71. package/src/vite-plugin/manageOptimizedDeps.ts +2 -0
  72. package/tools/isLikelyDevServer.d.ts +1 -0
  73. package/tools/isLikelyDevServer.js +17 -0
  74. package/tools/isLikelyDevServer.js.map +1 -0
  75. package/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +2 -4
  76. package/tools/lazySessionStorage.js +84 -0
  77. package/tools/lazySessionStorage.js.map +1 -0
  78. package/vite-plugin/manageOptimizedDeps.js +1 -0
  79. package/vite-plugin/manageOptimizedDeps.js.map +1 -1
  80. package/esm/tools/EphemeralSessionStorage.js +0 -143
  81. package/esm/tools/EphemeralSessionStorage.js.map +0 -1
  82. package/src/tools/EphemeralSessionStorage.ts +0 -225
  83. package/tools/EphemeralSessionStorage.js +0 -146
  84. package/tools/EphemeralSessionStorage.js.map +0 -1
@@ -4,7 +4,7 @@ import {
4
4
  type User as OidcClientTsUser,
5
5
  InMemoryWebStorage
6
6
  } from "../vendor/frontend/oidc-client-ts";
7
- import type { OidcMetadata } from "./OidcMetadata";
7
+ import { type OidcMetadata, fetchOidcMetadata } from "./OidcMetadata";
8
8
  import { assert, is, type Equals } from "../tools/tsafe/assert";
9
9
  import { id } from "../tools/tsafe/id";
10
10
  import { setTimeout, clearTimeout } from "../tools/workerTimers";
@@ -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
@@ -49,10 +44,14 @@ import { createGetIsNewBrowserSession } from "./isNewBrowserSession";
49
44
  import { getIsOnline } from "../tools/getIsOnline";
50
45
  import { isKeycloak } from "../keycloak/isKeycloak";
51
46
  import { INFINITY_TIME } from "../tools/INFINITY_TIME";
52
- import type { WELL_KNOWN_PATH } from "./diagnostic";
53
- import { getIsValidRemoteJson } from "../tools/getIsValidRemoteJson";
54
47
  import { prShouldLoadApp } from "./prShouldLoadApp";
55
48
  import { getBASE_URL } from "./BASE_URL";
49
+ import { getIsLikelyDevServer } from "../tools/isLikelyDevServer";
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
@@ -432,92 +413,212 @@ export async function createOidc_nonMemoized<
432
413
 
433
414
  const stateUrlParamValue_instance = generateStateUrlParamValue();
434
415
 
416
+ const oidcMetadata = __metadata ?? (await fetchOidcMetadata({ issuerUri }));
417
+
435
418
  const canUseIframe = (() => {
436
419
  if (noIframe) {
437
420
  return false;
438
421
  }
439
422
 
440
423
  third_party_cookies: {
441
- const isOidcServerThirdPartyRelativeToApp =
442
- getHaveSharedParentDomain({
443
- url1: window.location.origin,
444
- url2: issuerUri
445
- }) === false;
446
-
447
- if (!isOidcServerThirdPartyRelativeToApp) {
448
- break third_party_cookies;
424
+ if (oidcMetadata === undefined) {
425
+ return false;
449
426
  }
450
427
 
451
- const isGoogleChrome = (() => {
452
- const ua = navigator.userAgent;
453
- const vendor = navigator.vendor;
428
+ const { authorization_endpoint } = oidcMetadata;
454
429
 
455
- return (
456
- /Chrome/.test(ua) && /Google Inc/.test(vendor) && !/Edg/.test(ua) && !/OPR/.test(ua)
457
- );
458
- })();
430
+ assert(
431
+ authorization_endpoint !== undefined,
432
+ "Missing authorization_endpoint on the provided __metadata"
433
+ );
434
+
435
+ const isOidcServerThirdPartyRelativeToApp = !getHaveSharedParentDomain({
436
+ url1: window.location.origin,
437
+ // TODO: No, here we should test against the authorization endpoint!
438
+ url2: authorization_endpoint
439
+ });
459
440
 
460
- if (window.location.origin.startsWith("http://localhost") && isGoogleChrome) {
441
+ if (!isOidcServerThirdPartyRelativeToApp) {
461
442
  break third_party_cookies;
462
443
  }
463
444
 
464
- log?.(
465
- [
466
- "Can't use iframe because your auth server is on a third party domain relative",
467
- "to the domain of your app and third party cookies are blocked by navigators."
468
- ].join(" ")
469
- );
445
+ const isLikelyDevServer = getIsLikelyDevServer();
470
446
 
471
- return false;
472
- }
447
+ const domain_auth = new URL(authorization_endpoint).origin.split("//")[1];
473
448
 
474
- // NOTE: Maybe not, it depend if the app can iframe itself.
475
- return true;
476
- })();
449
+ assert(domain_auth !== undefined, "33921384");
450
+
451
+ const domain_here = window.location.origin.split("//")[1];
452
+
453
+ let isWellKnownProviderDomain = false;
454
+ let isIp = false;
477
455
 
478
- let isUserStoreInMemoryOnly: boolean;
479
-
480
- const oidcClientTsUserManager = new OidcClientTsUserManager({
481
- stateUrlParamValue: stateUrlParamValue_instance,
482
- authority: issuerUri,
483
- client_id: clientId,
484
- redirect_uri: homeUrlAndRedirectUri,
485
- silent_redirect_uri: homeUrlAndRedirectUri,
486
- post_logout_redirect_uri: homeUrlAndRedirectUri,
487
- response_mode: isKeycloak({ issuerUri }) ? "fragment" : "query",
488
- response_type: "code",
489
- scope: Array.from(new Set(["openid", ...scopes])).join(" "),
490
- automaticSilentRenew: false,
491
- userStore: new WebStorageStateStore({
492
- store: (() => {
493
- if (canUseIframe) {
494
- isUserStoreInMemoryOnly = true;
495
- return new InMemoryWebStorage();
456
+ const suggestedDeployments = (() => {
457
+ if (/^(?:\d{1,3}\.){3}\d{1,3}$|^\[?[A-Fa-f0-9:]+\]?$/.test(domain_auth)) {
458
+ isIp = true;
459
+ return [];
496
460
  }
497
461
 
498
- isUserStoreInMemoryOnly = false;
462
+ const baseDomain = (() => {
463
+ const segments = domain_auth.split(".");
499
464
 
500
- const storage = createEphemeralSessionStorage({
501
- sessionStorageTtlMs: 3 * 60_000
502
- });
465
+ if (segments.length >= 3) {
466
+ segments.shift();
467
+ }
468
+ return segments.join(".");
469
+ })();
503
470
 
504
- const { evtRequestToPersistTokens } = globalContext;
471
+ {
472
+ const baseDomain_low = baseDomain.toLowerCase();
505
473
 
506
- evtRequestToPersistTokens.subscribe(({ configIdOfInstancePostingTheRequest }) => {
507
- if (configIdOfInstancePostingTheRequest === configId) {
508
- return;
474
+ if (
475
+ baseDomain_low.includes("auth0") ||
476
+ baseDomain_low.includes("clerk") ||
477
+ baseDomain_low.includes("microsoft") ||
478
+ baseDomain_low.includes("okta") ||
479
+ baseDomain_low.includes("aws")
480
+ ) {
481
+ isWellKnownProviderDomain = true;
482
+ return [];
509
483
  }
484
+ }
510
485
 
511
- storage.persistCurrentStateAndSubsequentChanges();
512
- });
486
+ const baseUrl = new URL(homeUrlAndRedirectUri).pathname;
513
487
 
514
- return storage;
515
- })()
516
- }),
517
- stateStore: new WebStorageStateStore({ store: localStorage, prefix: STATE_STORE_KEY_PREFIX }),
518
- client_secret: __unsafe_clientSecret,
519
- metadata: __metadata
520
- });
488
+ return [
489
+ `myapp.${baseDomain}`,
490
+ baseDomain === domain_auth ? undefined : baseDomain,
491
+ `${baseDomain}/${baseUrl === "/" ? "dashboard" : baseUrl}`
492
+ ].filter(x => x !== undefined);
493
+ })();
494
+
495
+ if (isLikelyDevServer) {
496
+ log?.(
497
+ [
498
+ "Detected localhost environment.",
499
+ "\nWhen reloading while logged in, you will briefly see",
500
+ "some URL params appear in the address bar.",
501
+ "\nThis happens because session restore via iframe is disabled,",
502
+ "the browser treats your auth server as a third party.",
503
+ `\nAuth server: ${domain_auth}`,
504
+ `\nApp domain: ${domain_here}`,
505
+ ...(() => {
506
+ if (isIp) {
507
+ return [];
508
+ }
509
+
510
+ if (isWellKnownProviderDomain) {
511
+ return [
512
+ "\nYou seem to be using a well-known auth provider.",
513
+ "Check your provider's docs, some allow configuring",
514
+ `a your custom domain at least for the authorization endpoint.`,
515
+ "\nIf configured, oidc-spa will restore sessions silently",
516
+ "and improve the user experience."
517
+ ];
518
+ }
519
+
520
+ return [
521
+ "\nOnce deployed under the same root domain as your auth server,",
522
+ "oidc-spa will use iframes to restore sessions silently.",
523
+ "\nSuggested deployments:",
524
+ ...suggestedDeployments.map(d => `\n • ${d}`)
525
+ ];
526
+ })(),
527
+ "\n\nMore info:",
528
+ "https://docs.oidc-spa.dev/v/v8/resources/end-of-third-party-cookies#when-are-cookies-considered-third-party"
529
+ ].join(" ")
530
+ );
531
+ } else {
532
+ log?.(
533
+ [
534
+ "Silent session restore via iframe is disabled.",
535
+ `\nAuth server: ${domain_auth}`,
536
+ `App domain: ${domain_here}`,
537
+ "\nThey do not share a common root domain.",
538
+ ...(() => {
539
+ if (isIp) {
540
+ return [];
541
+ }
542
+
543
+ if (isWellKnownProviderDomain) {
544
+ return [
545
+ "\nYou seem to be using a well-known auth provider.",
546
+ "Check if you can configure a custom auth domain.",
547
+ "\nIf so, oidc-spa can restore sessions silently",
548
+ "and improve the user experience."
549
+ ];
550
+ }
551
+
552
+ return [
553
+ "\nTo improve the experience, here are some examples of deployment for your app:",
554
+ ...suggestedDeployments.map(d => `\n • ${d}`)
555
+ ];
556
+ })(),
557
+ "\nMore info:",
558
+ "https://docs.oidc-spa.dev/v/v8/resources/end-of-third-party-cookies#when-are-cookies-considered-third-party"
559
+ ].join(" ")
560
+ );
561
+ }
562
+
563
+ return false;
564
+ }
565
+
566
+ return true;
567
+ })();
568
+
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
+ }
579
+
580
+ const oidcClientTsUserManager =
581
+ oidcMetadata === undefined
582
+ ? createObjectThatThrowsIfAccessed<OidcClientTsUserManager>({
583
+ debugMessage: "oidc-spa: Wrong assertion 43943"
584
+ })
585
+ : new OidcClientTsUserManager({
586
+ stateUrlParamValue: stateUrlParamValue_instance,
587
+ authority: issuerUri,
588
+ client_id: clientId,
589
+ redirect_uri: homeUrlAndRedirectUri,
590
+ silent_redirect_uri: homeUrlAndRedirectUri,
591
+ post_logout_redirect_uri: homeUrlAndRedirectUri,
592
+ response_mode: isKeycloak({ issuerUri }) ? "fragment" : "query",
593
+ response_type: "code",
594
+ scope: Array.from(new Set(["openid", ...scopes])).join(" "),
595
+ automaticSilentRenew: false,
596
+ userStore: new WebStorageStateStore({
597
+ store: (() => {
598
+ if (canUseIframe) {
599
+ return new InMemoryWebStorage();
600
+ }
601
+
602
+ const storage = createLazySessionStorage();
603
+
604
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
605
+ storage.persistCurrentStateAndSubsequentChanges();
606
+ } else {
607
+ evtIsThereMoreThanOneInstanceThatCantUserIframes.subscribe(() => {
608
+ storage.persistCurrentStateAndSubsequentChanges();
609
+ });
610
+ }
611
+
612
+ return storage;
613
+ })()
614
+ }),
615
+ stateStore: new WebStorageStateStore({
616
+ store: localStorage,
617
+ prefix: STATE_STORE_KEY_PREFIX
618
+ }),
619
+ client_secret: __unsafe_clientSecret,
620
+ metadata: oidcMetadata
621
+ });
521
622
 
522
623
  const evtInitializationOutcomeUserNotLoggedIn = createEvt<void>();
523
624
 
@@ -547,75 +648,75 @@ export async function createOidc_nonMemoized<
547
648
  backFromAuthServer: Oidc.LoggedIn["backFromAuthServer"]; // Undefined is silent signin
548
649
  }
549
650
  > => {
550
- handle_redirect_auth_response: {
551
- let stateDataAndAuthResponse:
552
- | { stateData: StateData.Redirect; authResponse: AuthResponse }
553
- | undefined = undefined;
554
-
555
- get_stateData_and_authResponse: {
556
- from_memory: {
557
- const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
651
+ if (oidcMetadata === undefined) {
652
+ return (
653
+ await import("./diagnostic")
654
+ ).createWellKnownOidcConfigurationEndpointUnreachableInitializationError({
655
+ issuerUri
656
+ });
657
+ }
558
658
 
559
- if (authResponse === undefined) {
560
- break from_memory;
561
- }
659
+ restore_from_session_storage: {
660
+ if (canUseIframe) {
661
+ break restore_from_session_storage;
662
+ }
562
663
 
563
- const stateData = getStateData({ stateUrlParamValue: authResponse.state });
664
+ if (!evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
665
+ break restore_from_session_storage;
666
+ }
564
667
 
565
- if (stateData === undefined) {
566
- clearAuthResponse();
567
- break from_memory;
568
- }
668
+ let oidcClientTsUser: OidcClientTsUser | null;
569
669
 
570
- if (stateData.configId !== configId) {
571
- break from_memory;
572
- }
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
+ }
573
679
 
574
- assert(stateData.context === "redirect", "3229492");
680
+ if (oidcClientTsUser === null) {
681
+ break restore_from_session_storage;
682
+ }
575
683
 
576
- clearAuthResponse();
684
+ log?.("Session was restored from session storage");
577
685
 
578
- stateDataAndAuthResponse = { stateData, authResponse };
686
+ return {
687
+ oidcClientTsUser,
688
+ backFromAuthServer: undefined
689
+ };
690
+ }
579
691
 
580
- break get_stateData_and_authResponse;
581
- }
692
+ handle_redirect_auth_response: {
693
+ let stateDataAndAuthResponse:
694
+ | { stateData: StateData.Redirect; authResponse: AuthResponse }
695
+ | undefined = undefined;
582
696
 
583
- // from storage, this is for race condition in multiple instance
584
- // setup where one instance would need to redirect before
585
- // the authResponse in memory had the chance to be processed.
586
- // This can only happen if:
587
- // 1) There are multiple oidc instances in the App.
588
- // 2) They are instantiated in a non deterministic order.
589
- // 3) We can't use iframe
590
- // We practically never persist the auth response and do it only in session
591
- // an ephemeral session storage, when we know it's gonna be required.
592
- {
593
- const { authResponses } = getPersistedRedirectAuthResponses();
697
+ {
698
+ const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
594
699
 
595
- for (const authResponse of authResponses) {
596
- const stateData = getStateData({ stateUrlParamValue: authResponse.state });
700
+ if (authResponse === undefined) {
701
+ break handle_redirect_auth_response;
702
+ }
597
703
 
598
- if (stateData === undefined) {
599
- continue;
600
- }
704
+ const stateData = getStateData({ stateUrlParamValue: authResponse.state });
601
705
 
602
- if (stateData.configId !== configId) {
603
- continue;
604
- }
706
+ if (stateData === undefined) {
707
+ clearAuthResponse();
708
+ break handle_redirect_auth_response;
709
+ }
605
710
 
606
- assert(stateData.context === "redirect", "35935591");
711
+ if (stateData.configId !== configId) {
712
+ break handle_redirect_auth_response;
713
+ }
607
714
 
608
- setPersistedRedirectAuthResponses({
609
- authResponses: authResponses.filter(
610
- authResponse_i => authResponse_i !== authResponse
611
- )
612
- });
715
+ assert(stateData.context === "redirect", "3229492");
613
716
 
614
- stateDataAndAuthResponse = { stateData, authResponse };
717
+ clearAuthResponse();
615
718
 
616
- break get_stateData_and_authResponse;
617
- }
618
- }
719
+ stateDataAndAuthResponse = { stateData, authResponse };
619
720
  }
620
721
 
621
722
  if (stateDataAndAuthResponse === undefined) {
@@ -728,37 +829,6 @@ export async function createOidc_nonMemoized<
728
829
  }
729
830
  }
730
831
 
731
- // NOTE: We almost never persist tokens, we have to only to support edge case
732
- // of multiple oidc instance in a single App with no iframe support.
733
- restore_from_session_storage: {
734
- if (isUserStoreInMemoryOnly) {
735
- break restore_from_session_storage;
736
- }
737
-
738
- let oidcClientTsUser: OidcClientTsUser | null;
739
-
740
- try {
741
- oidcClientTsUser = await oidcClientTsUserManager.getUser();
742
- } catch {
743
- // NOTE: Not sure if it can throw, but let's be safe.
744
- oidcClientTsUser = null;
745
- try {
746
- await oidcClientTsUserManager.removeUser();
747
- } catch {}
748
- }
749
-
750
- if (oidcClientTsUser === null) {
751
- break restore_from_session_storage;
752
- }
753
-
754
- log?.("Restored the auth from ephemeral session storage");
755
-
756
- return {
757
- oidcClientTsUser,
758
- backFromAuthServer: undefined
759
- };
760
- }
761
-
762
832
  silent_login_if_possible_and_auto_login: {
763
833
  const persistedAuthState = getPersistedAuthState({ configId });
764
834
 
@@ -801,20 +871,6 @@ export async function createOidc_nonMemoized<
801
871
  }
802
872
 
803
873
  if (!canUseIframe) {
804
- if (
805
- !(await getIsValidRemoteJson(
806
- `${issuerUri}${id<typeof WELL_KNOWN_PATH>(
807
- "/.well-known/openid-configuration"
808
- )}`
809
- ))
810
- ) {
811
- return (
812
- await import("./diagnostic")
813
- ).createWellKnownOidcConfigurationEndpointUnreachableInitializationError({
814
- issuerUri
815
- });
816
- }
817
-
818
874
  break actual_silent_signin;
819
875
  }
820
876
 
@@ -835,26 +891,13 @@ export async function createOidc_nonMemoized<
835
891
 
836
892
  assert(result_loginSilent.outcome !== "token refreshed using refresh token", "876995");
837
893
 
838
- if (result_loginSilent.outcome === "failure") {
839
- switch (result_loginSilent.cause) {
840
- case "can't reach well-known oidc endpoint":
841
- return (
842
- await import("./diagnostic")
843
- ).createWellKnownOidcConfigurationEndpointUnreachableInitializationError({
844
- issuerUri
845
- });
846
- case "timeout":
847
- return (await import("./diagnostic")).createIframeTimeoutInitializationError(
848
- {
849
- redirectUri: homeUrlAndRedirectUri,
850
- clientId,
851
- issuerUri,
852
- noIframe
853
- }
854
- );
855
- }
856
-
857
- assert<Equals<typeof result_loginSilent.cause, never>>(false);
894
+ if (result_loginSilent.outcome === "timeout") {
895
+ return (await import("./diagnostic")).createIframeTimeoutInitializationError({
896
+ redirectUri: homeUrlAndRedirectUri,
897
+ clientId,
898
+ issuerUri,
899
+ noIframe
900
+ });
858
901
  }
859
902
 
860
903
  assert<Equals<typeof result_loginSilent.outcome, "got auth response from iframe">>();
@@ -899,8 +942,6 @@ export async function createOidc_nonMemoized<
899
942
  ) {
900
943
  log?.("Performing auto login with redirect");
901
944
 
902
- persistAuthState({ configId, state: undefined });
903
-
904
945
  completeLoginOrRefreshProcess();
905
946
 
906
947
  if (autoLogin && persistedAuthState !== "logged in") {
@@ -912,18 +953,16 @@ export async function createOidc_nonMemoized<
912
953
  getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError()
913
954
  });
914
955
 
915
- if (persistedAuthState === "logged in") {
916
- globalContext.evtRequestToPersistTokens.post({
917
- configIdOfInstancePostingTheRequest: configId
918
- });
919
- }
920
-
921
- const dCantFetchWellKnownEndpointOrNever = new Deferred<void | never>();
922
-
923
- loginOrGoToAuthServer({
956
+ await loginOrGoToAuthServer({
924
957
  action: "login",
925
958
  doForceReloadOnBfCache: true,
926
- redirectUrl: getRootRelativeOriginalLocationHref(),
959
+ redirectUrl: (() => {
960
+ if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
961
+ return window.location.href;
962
+ }
963
+
964
+ return getRootRelativeOriginalLocationHref();
965
+ })(),
927
966
  // NOTE: Wether or not it's the preferred behavior, pushing to history
928
967
  // only works on user interaction so it have to be false
929
968
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
@@ -940,19 +979,10 @@ export async function createOidc_nonMemoized<
940
979
 
941
980
  return "ensure no interaction";
942
981
  })(),
943
- onCantFetchWellKnownEndpointError: () => {
944
- dCantFetchWellKnownEndpointOrNever.resolve();
982
+ preRedirectHook: () => {
983
+ persistAuthState({ configId, state: undefined });
945
984
  }
946
985
  });
947
-
948
- await dCantFetchWellKnownEndpointOrNever.pr;
949
-
950
- return (
951
- await import("./diagnostic")
952
- ).createFailedToFetchTokenEndpointInitializationError({
953
- clientId,
954
- issuerUri
955
- });
956
986
  }
957
987
 
958
988
  if (authResponse_error !== undefined) {
@@ -1073,10 +1103,7 @@ export async function createOidc_nonMemoized<
1073
1103
  getPersistedAuthState({ configId }) === "explicitly logged out"
1074
1104
  ? "ensure interaction"
1075
1105
  : "directly redirect if active session show login otherwise",
1076
- onCantFetchWellKnownEndpointError: () => {
1077
- log?.("Login called but the auth server seems to be down..");
1078
- alert("Authentication unavailable please try again later.");
1079
- }
1106
+ preRedirectHook: undefined
1080
1107
  });
1081
1108
  },
1082
1109
  initializationError: undefined
@@ -1148,6 +1175,7 @@ export async function createOidc_nonMemoized<
1148
1175
  state: {
1149
1176
  stateDescription: "logged in",
1150
1177
  refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
1178
+ serverDateNow: currentTokens.getServerDateNow(),
1151
1179
  idleSessionLifetimeInSeconds
1152
1180
  }
1153
1181
  });
@@ -1297,10 +1325,6 @@ export async function createOidc_nonMemoized<
1297
1325
  prUnlock: new Promise<never>(() => {})
1298
1326
  });
1299
1327
 
1300
- globalContext.evtRequestToPersistTokens.post({
1301
- configIdOfInstancePostingTheRequest: configId
1302
- });
1303
-
1304
1328
  await loginOrGoToAuthServer({
1305
1329
  action: "login",
1306
1330
  redirectUrl: window.location.href,
@@ -1309,15 +1333,7 @@ export async function createOidc_nonMemoized<
1309
1333
  transformUrlBeforeRedirect_local: undefined,
1310
1334
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
1311
1335
  interaction: "directly redirect if active session show login otherwise",
1312
- onCantFetchWellKnownEndpointError: () => {
1313
- log?.(
1314
- [
1315
- "The auth server seems to be down while we needed to refresh the token",
1316
- "with a full page redirect. Reloading the page"
1317
- ].join(" ")
1318
- );
1319
- window.location.reload();
1320
- }
1336
+ preRedirectHook: undefined
1321
1337
  });
1322
1338
  assert(false, "136134");
1323
1339
  };
@@ -1352,10 +1368,10 @@ export async function createOidc_nonMemoized<
1352
1368
  log
1353
1369
  });
1354
1370
 
1355
- if (result_loginSilent.outcome === "failure") {
1371
+ if (result_loginSilent.outcome === "timeout") {
1356
1372
  log?.(
1357
1373
  [
1358
- `Silent refresh of the token failed with ${result_loginSilent.cause}.`,
1374
+ `Silent refresh of the token failed the iframe didn't post a response (timeout).`,
1359
1375
  `This isn't recoverable, reloading the page.`
1360
1376
  ].join(" ")
1361
1377
  );
@@ -1438,6 +1454,7 @@ export async function createOidc_nonMemoized<
1438
1454
  state: {
1439
1455
  stateDescription: "logged in",
1440
1456
  refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
1457
+ serverDateNow: currentTokens.getServerDateNow(),
1441
1458
  idleSessionLifetimeInSeconds
1442
1459
  }
1443
1460
  });
@@ -1545,11 +1562,7 @@ export async function createOidc_nonMemoized<
1545
1562
  action: "go to auth server",
1546
1563
  redirectUrl: redirectUrl ?? window.location.href,
1547
1564
  extraQueryParams_local: extraQueryParams,
1548
- transformUrlBeforeRedirect_local: transformUrlBeforeRedirect,
1549
- onCantFetchWellKnownEndpointError: () => {
1550
- log?.("goToAuthServer called but the auth server seems to be down..");
1551
- alert("Authentication unavailable please try again later.");
1552
- }
1565
+ transformUrlBeforeRedirect_local: transformUrlBeforeRedirect
1553
1566
  }),
1554
1567
  backFromAuthServer: resultOfLoginProcess.backFromAuthServer,
1555
1568
  isNewBrowserSession: (() => {