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.
- package/README.md +5 -7
- package/core/createOidc.js +81 -33
- package/core/createOidc.js.map +1 -1
- package/core/diagnostic.d.ts +1 -0
- package/core/diagnostic.js +5 -4
- package/core/diagnostic.js.map +1 -1
- package/core/iframeMessageProtection.d.ts +0 -1
- package/core/iframeMessageProtection.js +2 -4
- package/core/iframeMessageProtection.js.map +1 -1
- package/core/isNewBrowserSession.d.ts +1 -1
- package/core/isNewBrowserSession.js +2 -2
- package/core/isNewBrowserSession.js.map +1 -1
- package/core/loginOrGoToAuthServer.d.ts +3 -2
- package/core/loginOrGoToAuthServer.js +45 -29
- package/core/loginOrGoToAuthServer.js.map +1 -1
- package/core/loginSilent.js +10 -2
- package/core/loginSilent.js.map +1 -1
- package/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
- package/esm/core/createOidc.js +82 -34
- package/esm/core/createOidc.js.map +1 -1
- package/esm/core/diagnostic.d.ts +1 -0
- package/esm/core/diagnostic.js +1 -1
- package/esm/core/diagnostic.js.map +1 -1
- package/esm/core/iframeMessageProtection.d.ts +0 -1
- package/esm/core/iframeMessageProtection.js +2 -4
- package/esm/core/iframeMessageProtection.js.map +1 -1
- package/esm/core/isNewBrowserSession.d.ts +1 -1
- package/esm/core/isNewBrowserSession.js +2 -2
- package/esm/core/isNewBrowserSession.js.map +1 -1
- package/esm/core/loginOrGoToAuthServer.d.ts +3 -2
- package/esm/core/loginOrGoToAuthServer.js +44 -28
- package/esm/core/loginOrGoToAuthServer.js.map +1 -1
- package/esm/core/loginSilent.js +10 -2
- package/esm/core/loginSilent.js.map +1 -1
- package/esm/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
- package/package.json +1 -1
- package/src/core/createOidc.ts +106 -36
- package/src/core/diagnostic.ts +2 -2
- package/src/core/iframeMessageProtection.ts +3 -11
- package/src/core/isNewBrowserSession.ts +3 -3
- package/src/core/loginOrGoToAuthServer.ts +59 -31
- package/src/core/loginSilent.ts +11 -2
- package/src/core/ongoingLoginOrRefreshProcesses.ts +8 -0
- package/vendor/backend/tsafe.js +1 -1
package/src/core/createOidc.ts
CHANGED
|
@@ -38,7 +38,7 @@ import { createEvt } from "../tools/Evt";
|
|
|
38
38
|
import { getHaveSharedParentDomain } from "../tools/haveSharedParentDomain";
|
|
39
39
|
import {
|
|
40
40
|
createLoginOrGoToAuthServer,
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
468
|
+
evtInitializationOutcomeUserNotLoggedIn,
|
|
467
469
|
log
|
|
468
470
|
});
|
|
469
471
|
|
|
470
472
|
const { getIsNewBrowserSession } = createGetIsNewBrowserSession({
|
|
471
473
|
configId,
|
|
472
|
-
|
|
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
|
-
|
|
844
|
+
evtInitializationOutcomeUserNotLoggedIn.post();
|
|
837
845
|
}
|
|
838
846
|
|
|
839
847
|
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
840
|
-
prUnlock:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
|
1541
|
+
(function scheduleTokenRefreshToKeepSessionAlive() {
|
|
1499
1542
|
if (!currentTokens.hasRefreshToken && !canUseIframe) {
|
|
1500
1543
|
log?.(
|
|
1501
1544
|
[
|
|
1502
|
-
"
|
|
1503
|
-
"
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
1683
|
+
scheduleTokenRefreshToKeepSessionAlive();
|
|
1614
1684
|
});
|
|
1615
1685
|
})();
|
|
1616
1686
|
|
package/src/core/diagnostic.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
-
|
|
5
|
+
evtInitializationOutcomeUserNotLoggedIn: NonPostableEvt<void>;
|
|
6
6
|
}) {
|
|
7
|
-
const { configId,
|
|
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 } =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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(
|
|
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 } =
|
|
365
|
+
const { unsubscribe } = evtInitializationOutcomeUserNotLoggedIn.subscribe(() => {
|
|
334
366
|
unsubscribe();
|
|
335
367
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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 {
|
package/src/core/loginSilent.ts
CHANGED
|
@@ -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;
|
package/vendor/backend/tsafe.js
CHANGED
|
@@ -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
|
|
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;
|