oidc-spa 8.1.5-rc.1 → 8.1.5

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 (44) hide show
  1. package/README.md +5 -7
  2. package/core/createOidc.js +81 -33
  3. package/core/createOidc.js.map +1 -1
  4. package/core/diagnostic.d.ts +1 -0
  5. package/core/diagnostic.js +5 -4
  6. package/core/diagnostic.js.map +1 -1
  7. package/core/iframeMessageProtection.d.ts +0 -1
  8. package/core/iframeMessageProtection.js +2 -4
  9. package/core/iframeMessageProtection.js.map +1 -1
  10. package/core/isNewBrowserSession.d.ts +1 -1
  11. package/core/isNewBrowserSession.js +2 -2
  12. package/core/isNewBrowserSession.js.map +1 -1
  13. package/core/loginOrGoToAuthServer.d.ts +3 -2
  14. package/core/loginOrGoToAuthServer.js +45 -29
  15. package/core/loginOrGoToAuthServer.js.map +1 -1
  16. package/core/loginSilent.js +10 -2
  17. package/core/loginSilent.js.map +1 -1
  18. package/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
  19. package/esm/core/createOidc.js +82 -34
  20. package/esm/core/createOidc.js.map +1 -1
  21. package/esm/core/diagnostic.d.ts +1 -0
  22. package/esm/core/diagnostic.js +1 -1
  23. package/esm/core/diagnostic.js.map +1 -1
  24. package/esm/core/iframeMessageProtection.d.ts +0 -1
  25. package/esm/core/iframeMessageProtection.js +2 -4
  26. package/esm/core/iframeMessageProtection.js.map +1 -1
  27. package/esm/core/isNewBrowserSession.d.ts +1 -1
  28. package/esm/core/isNewBrowserSession.js +2 -2
  29. package/esm/core/isNewBrowserSession.js.map +1 -1
  30. package/esm/core/loginOrGoToAuthServer.d.ts +3 -2
  31. package/esm/core/loginOrGoToAuthServer.js +44 -28
  32. package/esm/core/loginOrGoToAuthServer.js.map +1 -1
  33. package/esm/core/loginSilent.js +10 -2
  34. package/esm/core/loginSilent.js.map +1 -1
  35. package/esm/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/core/createOidc.ts +106 -36
  38. package/src/core/diagnostic.ts +2 -2
  39. package/src/core/iframeMessageProtection.ts +3 -11
  40. package/src/core/isNewBrowserSession.ts +3 -3
  41. package/src/core/loginOrGoToAuthServer.ts +59 -31
  42. package/src/core/loginSilent.ts +11 -2
  43. package/src/core/ongoingLoginOrRefreshProcesses.ts +8 -0
  44. package/vendor/backend/tsafe.js +1 -1
@@ -38,7 +38,7 @@ import { createEvt } from "../tools/Evt";
38
38
  import { getHaveSharedParentDomain } from "../tools/haveSharedParentDomain";
39
39
  import {
40
40
  createLoginOrGoToAuthServer,
41
- getPrSafelyRestoredFromBfCacheAfterLoginBackNavigation
41
+ getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError
42
42
  } from "./loginOrGoToAuthServer";
43
43
  import { createEphemeralSessionStorage } from "../tools/EphemeralSessionStorage";
44
44
  import {
@@ -49,6 +49,8 @@ import { createGetIsNewBrowserSession } from "./isNewBrowserSession";
49
49
  import { getIsOnline } from "../tools/getIsOnline";
50
50
  import { isKeycloak } from "../keycloak/isKeycloak";
51
51
  import { INFINITY_TIME } from "../tools/INFINITY_TIME";
52
+ import type { WELL_KNOWN_PATH } from "./diagnostic";
53
+ import { getIsValidRemoteJson } from "../tools/getIsValidRemoteJson";
52
54
 
53
55
  // NOTE: Replaced at build time
54
56
  const VERSION = "{{OIDC_SPA_VERSION}}";
@@ -454,7 +456,7 @@ export async function createOidc_nonMemoized<
454
456
  metadata: __metadata
455
457
  });
456
458
 
457
- const evtIsUserLoggedIn = createEvt<boolean>();
459
+ const evtInitializationOutcomeUserNotLoggedIn = createEvt<void>();
458
460
 
459
461
  const { loginOrGoToAuthServer } = createLoginOrGoToAuthServer({
460
462
  configId,
@@ -463,23 +465,13 @@ export async function createOidc_nonMemoized<
463
465
  getExtraQueryParams,
464
466
  getExtraTokenParams,
465
467
  homeUrl: homeUrlAndRedirectUri,
466
- evtIsUserLoggedIn,
468
+ evtInitializationOutcomeUserNotLoggedIn,
467
469
  log
468
470
  });
469
471
 
470
472
  const { getIsNewBrowserSession } = createGetIsNewBrowserSession({
471
473
  configId,
472
- evtUserNotLoggedIn: (() => {
473
- const evt = createEvt<void>();
474
-
475
- evtIsUserLoggedIn.subscribe(isUserLoggedIn => {
476
- if (!isUserLoggedIn) {
477
- evt.post();
478
- }
479
- });
480
-
481
- return evt;
482
- })()
474
+ evtInitializationOutcomeUserNotLoggedIn
483
475
  });
484
476
 
485
477
  const { completeLoginOrRefreshProcess } = await startLoginOrRefreshProcess();
@@ -673,6 +665,8 @@ export async function createOidc_nonMemoized<
673
665
  }
674
666
  }
675
667
 
668
+ // NOTE: We almost never persist tokens, we have to only to support edge case
669
+ // of multiple oidc instance in a single App with no iframe support.
676
670
  restore_from_session_storage: {
677
671
  if (isUserStoreInMemoryOnly) {
678
672
  break restore_from_session_storage;
@@ -744,6 +738,20 @@ export async function createOidc_nonMemoized<
744
738
  }
745
739
 
746
740
  if (!canUseIframe) {
741
+ if (
742
+ !(await getIsValidRemoteJson(
743
+ `${issuerUri}${id<typeof WELL_KNOWN_PATH>(
744
+ "/.well-known/openid-configuration"
745
+ )}`
746
+ ))
747
+ ) {
748
+ return (
749
+ await import("./diagnostic")
750
+ ).createWellKnownOidcConfigurationEndpointUnreachableInitializationError({
751
+ issuerUri
752
+ });
753
+ }
754
+
747
755
  break actual_silent_signin;
748
756
  }
749
757
 
@@ -833,11 +841,12 @@ export async function createOidc_nonMemoized<
833
841
  completeLoginOrRefreshProcess();
834
842
 
835
843
  if (autoLogin && persistedAuthState !== "logged in") {
836
- evtIsUserLoggedIn.post(false);
844
+ evtInitializationOutcomeUserNotLoggedIn.post();
837
845
  }
838
846
 
839
847
  await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
840
- prUnlock: new Promise<never>(() => {})
848
+ prUnlock:
849
+ getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError()
841
850
  });
842
851
 
843
852
  if (persistedAuthState === "logged in") {
@@ -846,7 +855,9 @@ export async function createOidc_nonMemoized<
846
855
  });
847
856
  }
848
857
 
849
- await loginOrGoToAuthServer({
858
+ const dCantFetchWellKnownEndpointOrNever = new Deferred<void | never>();
859
+
860
+ loginOrGoToAuthServer({
850
861
  action: "login",
851
862
  doForceReloadOnBfCache: true,
852
863
  redirectUrl: getRootRelativeOriginalLocationHref(),
@@ -865,9 +876,20 @@ export async function createOidc_nonMemoized<
865
876
  }
866
877
 
867
878
  return "ensure no interaction";
868
- })()
879
+ })(),
880
+ onCantFetchWellKnownEndpointError: () => {
881
+ dCantFetchWellKnownEndpointOrNever.resolve();
882
+ }
883
+ });
884
+
885
+ await dCantFetchWellKnownEndpointOrNever.pr;
886
+
887
+ return (
888
+ await import("./diagnostic")
889
+ ).createFailedToFetchTokenEndpointInitializationError({
890
+ clientId,
891
+ issuerUri
869
892
  });
870
- assert(false, "321389");
871
893
  }
872
894
 
873
895
  if (authResponse_error !== undefined) {
@@ -914,7 +936,7 @@ export async function createOidc_nonMemoized<
914
936
  break not_loggedIn_case;
915
937
  }
916
938
 
917
- evtIsUserLoggedIn.post(false);
939
+ evtInitializationOutcomeUserNotLoggedIn.post();
918
940
 
919
941
  if (getPersistedAuthState({ configId }) !== "explicitly logged out") {
920
942
  persistAuthState({ configId, state: undefined });
@@ -971,7 +993,8 @@ export async function createOidc_nonMemoized<
971
993
  transformUrlBeforeRedirect
972
994
  }) => {
973
995
  await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
974
- prUnlock: getPrSafelyRestoredFromBfCacheAfterLoginBackNavigation()
996
+ prUnlock:
997
+ getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError()
975
998
  });
976
999
 
977
1000
  return loginOrGoToAuthServer({
@@ -986,7 +1009,11 @@ export async function createOidc_nonMemoized<
986
1009
  interaction:
987
1010
  getPersistedAuthState({ configId }) === "explicitly logged out"
988
1011
  ? "ensure interaction"
989
- : "directly redirect if active session show login otherwise"
1012
+ : "directly redirect if active session show login otherwise",
1013
+ onCantFetchWellKnownEndpointError: () => {
1014
+ log?.("Login called but the auth server seems to be down..");
1015
+ alert("Authentication unavailable please try again later.");
1016
+ }
990
1017
  });
991
1018
  },
992
1019
  initializationError: undefined
@@ -1018,8 +1045,6 @@ export async function createOidc_nonMemoized<
1018
1045
 
1019
1046
  log?.("User is logged in");
1020
1047
 
1021
- evtIsUserLoggedIn.post(true);
1022
-
1023
1048
  let currentTokens = oidcClientTsUserToTokens({
1024
1049
  oidcClientTsUser: resultOfLoginProcess.oidcClientTsUser,
1025
1050
  decodedIdTokenSchema,
@@ -1199,7 +1224,16 @@ export async function createOidc_nonMemoized<
1199
1224
  extraQueryParams_local: undefined,
1200
1225
  transformUrlBeforeRedirect_local: undefined,
1201
1226
  doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
1202
- interaction: "directly redirect if active session show login otherwise"
1227
+ interaction: "directly redirect if active session show login otherwise",
1228
+ onCantFetchWellKnownEndpointError: () => {
1229
+ log?.(
1230
+ [
1231
+ "The auth server seems to be down while we needed to refresh the token",
1232
+ "with a full page redirect. Reloading the page"
1233
+ ].join(" ")
1234
+ );
1235
+ window.location.reload();
1236
+ }
1203
1237
  });
1204
1238
  assert(false, "136134");
1205
1239
  };
@@ -1235,10 +1269,15 @@ export async function createOidc_nonMemoized<
1235
1269
  });
1236
1270
 
1237
1271
  if (result_loginSilent.outcome === "failure") {
1238
- completeLoginOrRefreshProcess();
1239
- // NOTE: This is a configuration or network error, okay to throw,
1240
- // this exception doesn't have to be handle if it fails it fails.
1241
- throw new Error(result_loginSilent.cause);
1272
+ log?.(
1273
+ [
1274
+ `Silent refresh of the token failed with ${result_loginSilent.cause}.`,
1275
+ `This isn't recoverable, reloading the page.`
1276
+ ].join(" ")
1277
+ );
1278
+ window.location.reload();
1279
+ await new Promise<never>(() => {});
1280
+ assert(false);
1242
1281
  }
1243
1282
 
1244
1283
  let oidcClientTsUser: OidcClientTsUser;
@@ -1422,7 +1461,11 @@ export async function createOidc_nonMemoized<
1422
1461
  action: "go to auth server",
1423
1462
  redirectUrl: redirectUrl ?? window.location.href,
1424
1463
  extraQueryParams_local: extraQueryParams,
1425
- transformUrlBeforeRedirect_local: transformUrlBeforeRedirect
1464
+ transformUrlBeforeRedirect_local: transformUrlBeforeRedirect,
1465
+ onCantFetchWellKnownEndpointError: () => {
1466
+ log?.("goToAuthServer called but the auth server seems to be down..");
1467
+ alert("Authentication unavailable please try again later.");
1468
+ }
1426
1469
  }),
1427
1470
  backFromAuthServer: resultOfLoginProcess.backFromAuthServer,
1428
1471
  isNewBrowserSession: (() => {
@@ -1495,12 +1538,12 @@ export async function createOidc_nonMemoized<
1495
1538
  });
1496
1539
  })();
1497
1540
 
1498
- (function scheduleRenew() {
1541
+ (function scheduleTokenRefreshToKeepSessionAlive() {
1499
1542
  if (!currentTokens.hasRefreshToken && !canUseIframe) {
1500
1543
  log?.(
1501
1544
  [
1502
- "Disabling token auto refresh mechanism because we",
1503
- "have no way to renew the tokens without a full page reload"
1545
+ "Session keep-alive disabled: no refresh token and no iframe support. ",
1546
+ "Result: once tokens expire, continuing requires full reload."
1504
1547
  ].join(" ")
1505
1548
  );
1506
1549
  return;
@@ -1510,7 +1553,34 @@ export async function createOidc_nonMemoized<
1510
1553
  currentTokens.refreshTokenExpirationTime !== undefined &&
1511
1554
  currentTokens.refreshTokenExpirationTime >= INFINITY_TIME
1512
1555
  ) {
1513
- log?.("The refresh_token never expires, disabling auto-renewal mechanism");
1556
+ const warningLines: string[] = [];
1557
+
1558
+ if (scopes.includes("offline_access")) {
1559
+ warningLines.push("offline_access scope was explicitly requested.");
1560
+ } else if (isKeycloak({ issuerUri })) {
1561
+ warningLines.push("Keycloak likely enabled offline_access by default.");
1562
+ }
1563
+
1564
+ if (warningLines.length > 0) {
1565
+ warningLines.push(
1566
+ ...[
1567
+ "Misconfiguration: offline_access is for native apps, not SPAs. ",
1568
+ "You lose SSO and users must log in after every reload."
1569
+ ]
1570
+ );
1571
+ }
1572
+
1573
+ const logMessage = [
1574
+ "Refresh token never expires → no need to ping server.",
1575
+ "The backend session will not expire.",
1576
+ ...warningLines
1577
+ ].join(" ");
1578
+
1579
+ if (warningLines.length > 0) {
1580
+ console.warn(`oidc-spa: ${logMessage}`);
1581
+ } else {
1582
+ log?.(logMessage);
1583
+ }
1514
1584
  return;
1515
1585
  }
1516
1586
 
@@ -1526,7 +1596,7 @@ export async function createOidc_nonMemoized<
1526
1596
  if (msBeforeExpiration <= RENEW_MS_BEFORE_EXPIRES) {
1527
1597
  log?.(
1528
1598
  [
1529
- "Disabling auto renew mechanism. We just got fresh tokens",
1599
+ "Session keep-alive disabled. We just got fresh tokens",
1530
1600
  (() => {
1531
1601
  switch (typeOfTheTokenWeGotTheTtlFrom) {
1532
1602
  case "refresh":
@@ -1610,7 +1680,7 @@ export async function createOidc_nonMemoized<
1610
1680
  const { unsubscribe: tokenChangeUnsubscribe } = oidc_loggedIn.subscribeToTokensChange(() => {
1611
1681
  clearTimeout(timer);
1612
1682
  tokenChangeUnsubscribe();
1613
- scheduleRenew();
1683
+ scheduleTokenRefreshToKeepSessionAlive();
1614
1684
  });
1615
1685
  })();
1616
1686
 
@@ -2,13 +2,13 @@ import { OidcInitializationError } from "./OidcInitializationError";
2
2
  import { isKeycloak, createKeycloakUtils } from "../keycloak";
3
3
  import { getIsValidRemoteJson } from "../tools/getIsValidRemoteJson";
4
4
 
5
+ export const WELL_KNOWN_PATH = "/.well-known/openid-configuration";
6
+
5
7
  export async function createWellKnownOidcConfigurationEndpointUnreachableInitializationError(params: {
6
8
  issuerUri: string;
7
9
  }): Promise<OidcInitializationError> {
8
10
  const { issuerUri } = params;
9
11
 
10
- const WELL_KNOWN_PATH = "/.well-known/openid-configuration";
11
-
12
12
  const commonFallbackMessagePart = [
13
13
  `The OIDC server is either down or the issuerUri you provided is incorrect.`,
14
14
  `You provided the issuerUri: ${issuerUri}`,
@@ -38,20 +38,13 @@ function getSessionStorageKey(params: { stateUrlParamValue: string }) {
38
38
  return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
39
39
  }
40
40
 
41
- export async function initIframeMessageProtection(params: {
42
- stateUrlParamValue: string;
43
- log: typeof console.log | undefined;
44
- }) {
45
- const { stateUrlParamValue, log } = params;
41
+ export async function initIframeMessageProtection(params: { stateUrlParamValue: string }) {
42
+ const { stateUrlParamValue } = params;
46
43
 
47
44
  const { publicKey, privateKey } = await generateKeys();
48
45
 
49
46
  const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
50
47
 
51
- log?.(
52
- `Writing iframe messaging protection publicKey for state: ${stateUrlParamValue} at sessionStorage -> ${sessionStorageKey}`
53
- );
54
-
55
48
  setItem_real.call(sessionStorage, sessionStorageKey, publicKey);
56
49
 
57
50
  function getIsEncryptedAuthResponse(params: { message: unknown }): boolean {
@@ -76,7 +69,6 @@ export async function initIframeMessageProtection(params: {
76
69
  }
77
70
 
78
71
  function clearSessionStoragePublicKey() {
79
- log?.(`Clearing session storage public key at ${sessionStorageKey}`);
80
72
  sessionStorage.removeItem(sessionStorageKey);
81
73
  }
82
74
 
@@ -90,7 +82,7 @@ export async function encryptAuthResponse(params: { authResponse: AuthResponse }
90
82
  getSessionStorageKey({ stateUrlParamValue: authResponse.state })
91
83
  );
92
84
 
93
- assert(publicKey !== null, `2293302 no publicKey for state ${authResponse.state}`);
85
+ assert(publicKey !== null, "2293302");
94
86
 
95
87
  const { encryptedMessage: encryptedMessage_withoutPrefix } = await asymmetricEncrypt({
96
88
  publicKey,
@@ -2,14 +2,14 @@ import type { NonPostableEvt } from "../tools/Evt";
2
2
 
3
3
  export function createGetIsNewBrowserSession(params: {
4
4
  configId: string;
5
- evtUserNotLoggedIn: NonPostableEvt<void>;
5
+ evtInitializationOutcomeUserNotLoggedIn: NonPostableEvt<void>;
6
6
  }) {
7
- const { configId, evtUserNotLoggedIn } = params;
7
+ const { configId, evtInitializationOutcomeUserNotLoggedIn } = params;
8
8
 
9
9
  const SESSION_STORAGE_KEY = `oidc-spa.subject-id:${configId}`;
10
10
 
11
11
  {
12
- const { unsubscribe } = evtUserNotLoggedIn.subscribe(() => {
12
+ const { unsubscribe } = evtInitializationOutcomeUserNotLoggedIn.subscribe(() => {
13
13
  unsubscribe();
14
14
  sessionStorage.removeItem(SESSION_STORAGE_KEY);
15
15
  });
@@ -7,6 +7,7 @@ import type { NonPostableEvt } from "../tools/Evt";
7
7
  import { createStatefulEvt } from "../tools/StatefulEvt";
8
8
  import { Deferred } from "../tools/Deferred";
9
9
  import { addOrUpdateSearchParam, getAllSearchParams } from "../tools/urlSearchParams";
10
+ import { getIsOnline } from "../tools/getIsOnline";
10
11
 
11
12
  const globalContext = {
12
13
  evtHasLoginBeenCalled: createStatefulEvt(() => false)
@@ -19,6 +20,7 @@ namespace Params {
19
20
  redirectUrl: string;
20
21
  extraQueryParams_local: Record<string, string | undefined> | undefined;
21
22
  transformUrlBeforeRedirect_local: ((url: string) => string) | undefined;
23
+ onCantFetchWellKnownEndpointError: () => void;
22
24
  };
23
25
 
24
26
  export type Login = Common & {
@@ -36,7 +38,7 @@ namespace Params {
36
38
  };
37
39
  }
38
40
 
39
- export function getPrSafelyRestoredFromBfCacheAfterLoginBackNavigation() {
41
+ export function getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError() {
40
42
  const dOut = new Deferred<void>();
41
43
 
42
44
  const { unsubscribe } = globalContext.evtHasLoginBeenCalled.subscribe(hasLoginBeenCalled => {
@@ -63,7 +65,7 @@ export function createLoginOrGoToAuthServer(params: {
63
65
  getExtraTokenParams: (() => Record<string, string | undefined>) | undefined;
64
66
 
65
67
  homeUrl: string;
66
- evtIsUserLoggedIn: NonPostableEvt<boolean>;
68
+ evtInitializationOutcomeUserNotLoggedIn: NonPostableEvt<void>;
67
69
  log: typeof console.log | undefined;
68
70
  }) {
69
71
  const {
@@ -76,12 +78,11 @@ export function createLoginOrGoToAuthServer(params: {
76
78
  getExtraTokenParams,
77
79
 
78
80
  homeUrl,
79
- evtIsUserLoggedIn,
81
+ evtInitializationOutcomeUserNotLoggedIn,
82
+
80
83
  log
81
84
  } = params;
82
85
 
83
- const LOCAL_STORAGE_KEY_TO_CLEAR_WHEN_USER_LOGGED_IN = `oidc-spa.login-redirect-initiated:${configId}`;
84
-
85
86
  let lastPublicUrl: string | undefined = undefined;
86
87
 
87
88
  async function loginOrGoToAuthServer(params: Params): Promise<never> {
@@ -89,11 +90,24 @@ export function createLoginOrGoToAuthServer(params: {
89
90
  redirectUrl: redirectUrl_params,
90
91
  extraQueryParams_local,
91
92
  transformUrlBeforeRedirect_local,
93
+ onCantFetchWellKnownEndpointError: onCantFetchWellKnownEndpointError_params,
92
94
  ...rest
93
95
  } = params;
96
+ let onCantFetchWellKnownEndpointError = onCantFetchWellKnownEndpointError_params;
94
97
 
95
98
  log?.(`Calling loginOrGoToAuthServer ${JSON.stringify(params, null, 2)}`);
96
99
 
100
+ delay_until_online: {
101
+ const { isOnline, prOnline } = getIsOnline();
102
+ if (isOnline) {
103
+ break delay_until_online;
104
+ }
105
+ log?.(
106
+ "The browser seem offline, waiting to get back a connection before proceeding to login"
107
+ );
108
+ await prOnline;
109
+ }
110
+
97
111
  login_specific_handling: {
98
112
  if (rest.action !== "login") {
99
113
  break login_specific_handling;
@@ -125,17 +139,23 @@ export function createLoginOrGoToAuthServer(params: {
125
139
 
126
140
  bf_cache_handling: {
127
141
  if (rest.doForceReloadOnBfCache) {
128
- window.removeEventListener("pageshow", event => {
142
+ const callback = (event: { persisted: boolean }) => {
129
143
  if (!event.persisted) {
130
144
  return;
131
145
  }
132
146
  location.reload();
133
- });
147
+ };
148
+
149
+ window.addEventListener("pageshow", callback);
150
+
151
+ onCantFetchWellKnownEndpointError = () => {
152
+ window.removeEventListener("pageshow", callback);
153
+ onCantFetchWellKnownEndpointError_params();
154
+ };
155
+
134
156
  break bf_cache_handling;
135
157
  }
136
158
 
137
- localStorage.setItem(LOCAL_STORAGE_KEY_TO_CLEAR_WHEN_USER_LOGGED_IN, "true");
138
-
139
159
  const callback = (event: { persisted: boolean }) => {
140
160
  if (!event.persisted) {
141
161
  return;
@@ -156,21 +176,19 @@ export function createLoginOrGoToAuthServer(params: {
156
176
  window.history.back();
157
177
  }
158
178
  } else {
159
- log?.("The current page doesn't require auth...");
160
-
161
- if (
162
- localStorage.getItem(LOCAL_STORAGE_KEY_TO_CLEAR_WHEN_USER_LOGGED_IN) === null
163
- ) {
164
- log?.("but the user is now authenticated, reloading the page");
165
- location.reload();
166
- } else {
167
- log?.("and the user doesn't seem to be authenticated, avoiding a reload");
168
- globalContext.evtHasLoginBeenCalled.current = false;
169
- }
179
+ // NOTE: We know the user is not logged in because login can only be called when not logged in.
180
+ log?.("The current page doesn't require auth, avoiding reloading the page");
181
+ globalContext.evtHasLoginBeenCalled.current = false;
170
182
  }
171
183
  };
172
184
 
173
185
  window.addEventListener("pageshow", callback);
186
+
187
+ onCantFetchWellKnownEndpointError = () => {
188
+ window.removeEventListener("pageshow", callback);
189
+ globalContext.evtHasLoginBeenCalled.current = false;
190
+ onCantFetchWellKnownEndpointError_params();
191
+ };
174
192
  }
175
193
  }
176
194
 
@@ -327,21 +345,31 @@ export function createLoginOrGoToAuthServer(params: {
327
345
  extraTokenParams:
328
346
  getExtraTokenParams === undefined ? undefined : noUndefined(getExtraTokenParams())
329
347
  })
330
- .then(() => new Promise<never>(() => {}));
348
+ .then(
349
+ () => new Promise<never>(() => {}),
350
+ (error: Error) => {
351
+ if (error.message === "Failed to fetch") {
352
+ // NOTE: See ./loginSilent for explanation.
353
+ onCantFetchWellKnownEndpointError();
354
+
355
+ return new Promise<never>(() => {});
356
+ }
357
+
358
+ // NOTE: Here, except error on our understanding there can't be any other
359
+ // error.
360
+ assert(false, "30442320");
361
+ }
362
+ );
331
363
  }
332
364
 
333
- const { unsubscribe } = evtIsUserLoggedIn.subscribe(isLoggedIn => {
365
+ const { unsubscribe } = evtInitializationOutcomeUserNotLoggedIn.subscribe(() => {
334
366
  unsubscribe();
335
367
 
336
- if (isLoggedIn) {
337
- localStorage.removeItem(LOCAL_STORAGE_KEY_TO_CLEAR_WHEN_USER_LOGGED_IN);
338
- } else {
339
- const realPushState = history.pushState.bind(history);
340
- history.pushState = function pushState(...args) {
341
- lastPublicUrl = window.location.href;
342
- return realPushState(...args);
343
- };
344
- }
368
+ const realPushState = history.pushState.bind(history);
369
+ history.pushState = function pushState(...args) {
370
+ lastPublicUrl = window.location.href;
371
+ return realPushState(...args);
372
+ };
345
373
  });
346
374
 
347
375
  return {
@@ -12,6 +12,7 @@ import { getIsDev } from "../tools/isDev";
12
12
  import { type AuthResponse } from "./AuthResponse";
13
13
  import { addOrUpdateSearchParam } from "../tools/urlSearchParams";
14
14
  import { initIframeMessageProtection } from "./iframeMessageProtection";
15
+ import { getIsOnline } from "../tools/getIsOnline";
15
16
 
16
17
  type ResultOfLoginSilent =
17
18
  | {
@@ -55,6 +56,15 @@ export async function loginSilent(params: {
55
56
  log
56
57
  } = params;
57
58
 
59
+ delay_until_online: {
60
+ const { isOnline, prOnline } = getIsOnline();
61
+ if (isOnline) {
62
+ break delay_until_online;
63
+ }
64
+ log?.("The browser seems offline, waiting to get back a connection before proceeding to login");
65
+ await prOnline;
66
+ }
67
+
58
68
  const dResult = new Deferred<ResultOfLoginSilent>();
59
69
 
60
70
  const timeoutDelayMs: number = (() => {
@@ -80,8 +90,7 @@ export async function loginSilent(params: {
80
90
 
81
91
  const { decodeEncryptedAuth, getIsEncryptedAuthResponse, clearSessionStoragePublicKey } =
82
92
  await initIframeMessageProtection({
83
- stateUrlParamValue: stateUrlParamValue_instance,
84
- log
93
+ stateUrlParamValue: stateUrlParamValue_instance
85
94
  });
86
95
 
87
96
  let clearTimeouts: (params: { wasSuccess: boolean }) => void;
@@ -32,6 +32,14 @@ export async function startLoginOrRefreshProcess(): Promise<{
32
32
  }
33
33
 
34
34
  export async function waitForAllOtherOngoingLoginOrRefreshProcessesToComplete(params: {
35
+ // NOTE: when providing
36
+ // - prUnlock: new Promise<never>(()=>{}); All the subsequent startLoginOrRefreshProcess()
37
+ // will never resolve: No lock can be acquired anymore in this app instance.
38
+ // - prUnlock: Promise.resolve(); The subsequent startLoginOrRefreshProcess() will resolve
39
+ // normally: The lock can be acquired again
40
+ // - prUnlock: prMightOrMightNotResolve. The lock will be acquirable again assuming the pr ever resolves.
41
+ //
42
+ // In any case it does not affect the call that are already running.
35
43
  prUnlock: Promise<void>;
36
44
  }): Promise<void> {
37
45
  const { prUnlock } = params;
@@ -1,2 +1,2 @@
1
- (()=>{"use strict";var e={720:function(e,r){var t,n=this&&this.__extends||(t=function(e,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,r){e.__proto__=r}||function(e,r){for(var t in r)Object.prototype.hasOwnProperty.call(r,t)&&(e[t]=r[t])},t(e,r)},function(e,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)});Object.defineProperty(r,"__esModule",{value:!0}),r.AssertionError=void 0,r.assert=function e(r,t){if(0===arguments.length&&(r=!0),void 0===i){if(!r){var n=new o("function"==typeof t?t():t);throw Error.captureStackTrace&&Error.captureStackTrace(n,e),n}}else i=void 0},r.is=function(e){var r={};if(void 0!==i)throw i=void 0,new Error(u);return i=r,Promise.resolve().then((function(){if(i===r)throw new Error(u)})),null};var o=function(e){function r(r){var t=this.constructor,n=e.call(this,"Wrong assertion encountered"+(r?': "'.concat(r,'"'):""))||this;return n.originalMessage=r,Object.setPrototypeOf(n,t.prototype),n.stack,n}return n(r,e),r}(Error);r.AssertionError=o;var i=void 0,u="Wrong usage of the `is` function refer to https://docs.tsafe.dev/is"},202:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.exclude=function(e){var r=e instanceof Object?function(r){return e.indexOf(r)<0}:function(r){return r!==e};return function(e){return r(e)}}},135:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.id=void 0,r.id=function(e){return e}},952:function(e,r){var t=this&&this.__values||function(e){var r="function"==typeof Symbol&&Symbol.iterator,t=r&&e[r],n=0;if(t)return t.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(r?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(r,"__esModule",{value:!0}),r.isAmong=function(e,r){var n,o;try{for(var i=t(e),u=i.next();!u.done;u=i.next())if(u.value===r)return!0}catch(e){n={error:e}}finally{try{u&&!u.done&&(o=i.return)&&o.call(i)}finally{if(n)throw n.error}}return!1}}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}var n={};(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.exclude=e.isAmong=e.id=e.is=e.assert=void 0;var r=t(720);Object.defineProperty(e,"assert",{enumerable:!0,get:function(){return r.assert}}),Object.defineProperty(e,"is",{enumerable:!0,get:function(){return r.is}});var o=t(135);Object.defineProperty(e,"id",{enumerable:!0,get:function(){return o.id}});var i=t(952);Object.defineProperty(e,"isAmong",{enumerable:!0,get:function(){return i.isAmong}});var u=t(202);Object.defineProperty(e,"exclude",{enumerable:!0,get:function(){return u.exclude}})})(),module.exports=n})();
1
+ (()=>{"use strict";var e={720:function(e,r){var t,n=this&&this.__extends||(t=function(e,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,r){e.__proto__=r}||function(e,r){for(var t in r)Object.prototype.hasOwnProperty.call(r,t)&&(e[t]=r[t])},t(e,r)},function(e,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)});Object.defineProperty(r,"__esModule",{value:!0}),r.AssertionError=void 0,r.assert=function e(r,t){if(0===arguments.length&&(r=!0),void 0===i){if(!r){var n=new o("function"==typeof t?t():t);throw Error.captureStackTrace&&Error.captureStackTrace(n,e),n}}else i=void 0},r.is=function(e){var r={};if(void 0!==i)throw i=void 0,new Error(u);return i=r,Promise.resolve().then((function(){if(i===r)throw new Error(u)})),null};var o=function(e){function r(r){var t=this.constructor,n=e.call(this,"Wrong assertion encountered"+(r?': "'.concat(r,'"'):""))||this;return n.originalMessage=r,Object.setPrototypeOf(n,t.prototype),n}return n(r,e),r}(Error);r.AssertionError=o;var i=void 0,u="Wrong usage of the `is` function refer to https://docs.tsafe.dev/is"},202:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.exclude=function(e){var r=e instanceof Object?function(r){return e.indexOf(r)<0}:function(r){return r!==e};return function(e){return r(e)}}},135:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.id=void 0,r.id=function(e){return e}},952:function(e,r){var t=this&&this.__values||function(e){var r="function"==typeof Symbol&&Symbol.iterator,t=r&&e[r],n=0;if(t)return t.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(r?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(r,"__esModule",{value:!0}),r.isAmong=function(e,r){var n,o;try{for(var i=t(e),u=i.next();!u.done;u=i.next())if(u.value===r)return!0}catch(e){n={error:e}}finally{try{u&&!u.done&&(o=i.return)&&o.call(i)}finally{if(n)throw n.error}}return!1}}},r={};function t(n){var o=r[n];if(void 0!==o)return o.exports;var i=r[n]={exports:{}};return e[n].call(i.exports,i,i.exports,t),i.exports}var n={};(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0}),e.exclude=e.isAmong=e.id=e.is=e.assert=void 0;var r=t(720);Object.defineProperty(e,"assert",{enumerable:!0,get:function(){return r.assert}}),Object.defineProperty(e,"is",{enumerable:!0,get:function(){return r.is}});var o=t(135);Object.defineProperty(e,"id",{enumerable:!0,get:function(){return o.id}});var i=t(952);Object.defineProperty(e,"isAmong",{enumerable:!0,get:function(){return i.isAmong}});var u=t(202);Object.defineProperty(e,"exclude",{enumerable:!0,get:function(){return u.exclude}})})(),module.exports=n})();
2
2
  exports.__oidcSpaBundle = true;