oidc-spa 6.5.1 → 6.6.0
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/mock/oidc.js +6 -5
- package/mock/oidc.js.map +1 -1
- package/oidc/AuthResponse.d.ts +6 -0
- package/oidc/AuthResponse.js +59 -0
- package/oidc/AuthResponse.js.map +1 -0
- package/oidc/Oidc.d.ts +19 -8
- package/oidc/createOidc.d.ts +4 -4
- package/oidc/createOidc.js +359 -295
- package/oidc/createOidc.js.map +1 -1
- package/oidc/evtIsUserActive.d.ts +15 -0
- package/oidc/{isUserActive.js → evtIsUserActive.js} +29 -12
- package/oidc/evtIsUserActive.js.map +1 -0
- package/oidc/handleOidcCallback.d.ts +8 -1
- package/oidc/handleOidcCallback.js +68 -13
- package/oidc/handleOidcCallback.js.map +1 -1
- package/oidc/loginOrGoToAuthServer.d.ts +5 -4
- package/oidc/loginOrGoToAuthServer.js +190 -227
- package/oidc/loginOrGoToAuthServer.js.map +1 -1
- package/oidc/loginPropagationToOtherTabs.d.ts +17 -0
- package/oidc/loginPropagationToOtherTabs.js +41 -0
- package/oidc/loginPropagationToOtherTabs.js.map +1 -0
- package/oidc/loginSilent.d.ts +1 -5
- package/oidc/loginSilent.js +3 -51
- package/oidc/loginSilent.js.map +1 -1
- package/oidc/logoutPropagationToOtherTabs.js +1 -1
- package/oidc/logoutPropagationToOtherTabs.js.map +1 -1
- package/oidc/oidcClientTsUserToTokens.d.ts +1 -1
- package/oidc/oidcClientTsUserToTokens.js +45 -23
- package/oidc/oidcClientTsUserToTokens.js.map +1 -1
- package/oidc/ongoingLoginOrRefreshProcesses.d.ts +16 -0
- package/oidc/ongoingLoginOrRefreshProcesses.js +102 -0
- package/oidc/ongoingLoginOrRefreshProcesses.js.map +1 -0
- package/oidc/persistedAuthState.d.ts +16 -3
- package/oidc/persistedAuthState.js +35 -4
- package/oidc/persistedAuthState.js.map +1 -1
- package/package.json +36 -21
- package/react/react.js +8 -14
- package/react/react.js.map +1 -1
- package/src/mock/oidc.ts +14 -3
- package/src/oidc/AuthResponse.ts +26 -0
- package/src/oidc/Oidc.ts +19 -4
- package/src/oidc/createOidc.ts +233 -206
- package/src/oidc/{isUserActive.ts → evtIsUserActive.ts} +36 -10
- package/src/oidc/handleOidcCallback.ts +73 -12
- package/src/oidc/loginOrGoToAuthServer.ts +94 -87
- package/src/oidc/loginPropagationToOtherTabs.ts +63 -0
- package/src/oidc/loginSilent.ts +2 -20
- package/src/oidc/logoutPropagationToOtherTabs.ts +2 -2
- package/src/oidc/oidcClientTsUserToTokens.ts +74 -35
- package/src/oidc/ongoingLoginOrRefreshProcesses.ts +60 -0
- package/src/oidc/persistedAuthState.ts +66 -8
- package/src/react/react.tsx +8 -16
- package/src/tools/{ephemeralSessionStorage.ts → EphemeralSessionStorage.ts} +59 -27
- package/src/tools/Evt.ts +56 -0
- package/src/tools/StatefulEvt.ts +38 -0
- package/src/tools/parseKeycloakIssuerUri.ts +9 -1
- package/src/tools/subscribeToUserInteraction.ts +0 -1
- package/src/tools/workerTimers.ts +10 -12
- package/tools/EphemeralSessionStorage.d.ts +12 -0
- package/tools/{ephemeralSessionStorage.js → EphemeralSessionStorage.js} +29 -16
- package/tools/EphemeralSessionStorage.js.map +1 -0
- package/tools/Evt.d.ts +11 -0
- package/tools/{AwaitableEventEmitter.js → Evt.js} +24 -8
- package/tools/Evt.js.map +1 -0
- package/tools/StatefulEvt.d.ts +12 -0
- package/tools/StatefulEvt.js +24 -0
- package/tools/StatefulEvt.js.map +1 -0
- package/tools/parseKeycloakIssuerUri.js +5 -1
- package/tools/parseKeycloakIssuerUri.js.map +1 -1
- package/tools/subscribeToUserInteraction.js +2 -3
- package/tools/subscribeToUserInteraction.js.map +1 -1
- package/tools/workerTimers.js +11 -13
- package/tools/workerTimers.js.map +1 -1
- package/oidc/isUserActive.d.ts +0 -13
- package/oidc/isUserActive.js.map +0 -1
- package/src/tools/AwaitableEventEmitter.ts +0 -33
- package/src/tools/StatefulObservable.ts +0 -52
- package/tools/AwaitableEventEmitter.d.ts +0 -5
- package/tools/AwaitableEventEmitter.js.map +0 -1
- package/tools/StatefulObservable.d.ts +0 -12
- package/tools/StatefulObservable.js +0 -33
- package/tools/StatefulObservable.js.map +0 -1
- package/tools/ephemeralSessionStorage.d.ts +0 -3
- package/tools/ephemeralSessionStorage.js.map +0 -1
package/src/oidc/createOidc.ts
CHANGED
|
@@ -4,13 +4,12 @@ import {
|
|
|
4
4
|
type User as OidcClientTsUser,
|
|
5
5
|
InMemoryWebStorage
|
|
6
6
|
} from "../vendor/frontend/oidc-client-ts-and-jwt-decode";
|
|
7
|
-
import { id, assert, is, type Equals
|
|
7
|
+
import { id, assert, is, type Equals } from "../vendor/frontend/tsafe";
|
|
8
8
|
import { setTimeout, clearTimeout } from "../tools/workerTimers";
|
|
9
9
|
import { Deferred } from "../tools/Deferred";
|
|
10
10
|
import { decodeJwt } from "../tools/decodeJwt";
|
|
11
|
-
import {
|
|
11
|
+
import { createEvtIsUserActive } from "./evtIsUserActive";
|
|
12
12
|
import { createStartCountdown } from "../tools/startCountdown";
|
|
13
|
-
import type { StatefulObservable } from "../tools/StatefulObservable";
|
|
14
13
|
import { toHumanReadableDuration } from "../tools/toHumanReadableDuration";
|
|
15
14
|
import { toFullyQualifiedUrl } from "../tools/toFullyQualifiedUrl";
|
|
16
15
|
import {
|
|
@@ -19,23 +18,29 @@ import {
|
|
|
19
18
|
createIframeTimeoutInitializationError,
|
|
20
19
|
createWellKnownOidcConfigurationEndpointUnreachableInitializationError
|
|
21
20
|
} from "./OidcInitializationError";
|
|
22
|
-
import {
|
|
23
|
-
getStateData,
|
|
24
|
-
type StateData,
|
|
25
|
-
generateStateQueryParamValue,
|
|
26
|
-
STATE_STORE_KEY_PREFIX
|
|
27
|
-
} from "./StateData";
|
|
21
|
+
import { type StateData, generateStateQueryParamValue, STATE_STORE_KEY_PREFIX } from "./StateData";
|
|
28
22
|
import { notifyOtherTabsOfLogout, getPrOtherTabLogout } from "./logoutPropagationToOtherTabs";
|
|
23
|
+
import { notifyOtherTabsOfLogin, getPrOtherTabLogin } from "./loginPropagationToOtherTabs";
|
|
29
24
|
import { getConfigId } from "./configId";
|
|
30
25
|
import { oidcClientTsUserToTokens, getMsBeforeExpiration } from "./oidcClientTsUserToTokens";
|
|
31
|
-
import { loginSilent
|
|
32
|
-
import {
|
|
26
|
+
import { loginSilent } from "./loginSilent";
|
|
27
|
+
import { authResponseToUrl } from "./AuthResponse";
|
|
28
|
+
import { handleOidcCallback, retrieveRedirectAuthResponseAndStateData } from "./handleOidcCallback";
|
|
33
29
|
import { getPersistedAuthState, persistAuthState } from "./persistedAuthState";
|
|
34
30
|
import type { Oidc } from "./Oidc";
|
|
35
|
-
import { type
|
|
31
|
+
import { createEvt, type Evt } from "../tools/Evt";
|
|
36
32
|
import { getHaveSharedParentDomain } from "../tools/haveSharedParentDomain";
|
|
37
|
-
import {
|
|
38
|
-
|
|
33
|
+
import {
|
|
34
|
+
createLoginOrGoToAuthServer,
|
|
35
|
+
getPrSafelyRestoredFromBfCacheAfterLoginBackNavigation
|
|
36
|
+
} from "./loginOrGoToAuthServer";
|
|
37
|
+
import { createEphemeralSessionStorage } from "../tools/EphemeralSessionStorage";
|
|
38
|
+
import {
|
|
39
|
+
startLoginOrRefreshProcess,
|
|
40
|
+
waitForAllOtherOngoingLoginOrRefreshProcessesToComplete
|
|
41
|
+
} from "./ongoingLoginOrRefreshProcesses";
|
|
42
|
+
|
|
43
|
+
handleOidcCallback();
|
|
39
44
|
|
|
40
45
|
// NOTE: Replaced at build time
|
|
41
46
|
const VERSION = "{{OIDC_SPA_VERSION}}";
|
|
@@ -120,26 +125,22 @@ export type ParamsOfCreateOidc<
|
|
|
120
125
|
__unsafe_useIdTokenAsAccessToken?: boolean;
|
|
121
126
|
};
|
|
122
127
|
|
|
123
|
-
handleOidcCallback();
|
|
124
|
-
|
|
125
128
|
const GLOBAL_CONTEXT_KEY = "__oidc-spa.createOidc.globalContext";
|
|
126
129
|
|
|
127
130
|
declare global {
|
|
128
131
|
interface Window {
|
|
129
132
|
[GLOBAL_CONTEXT_KEY]: {
|
|
130
133
|
prOidcByConfigId: Map<string, Promise<Oidc<any>>>;
|
|
131
|
-
evtAuthResponseHandled: AwaitableEventEmitter<void>;
|
|
132
|
-
$isUserActive: StatefulObservable<boolean> | undefined;
|
|
133
134
|
hasLogoutBeenCalled: boolean;
|
|
135
|
+
evtRequestToPersistTokens: Evt<{ configIdOfInstancePostingTheRequest: string }>;
|
|
134
136
|
};
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
window[GLOBAL_CONTEXT_KEY] ??= {
|
|
139
141
|
prOidcByConfigId: new Map(),
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
hasLogoutBeenCalled: false
|
|
142
|
+
hasLogoutBeenCalled: false,
|
|
143
|
+
evtRequestToPersistTokens: createEvt()
|
|
143
144
|
};
|
|
144
145
|
|
|
145
146
|
const globalContext = window[GLOBAL_CONTEXT_KEY];
|
|
@@ -321,7 +322,7 @@ export async function createOidc_nonMemoized<
|
|
|
321
322
|
}
|
|
322
323
|
}
|
|
323
324
|
|
|
324
|
-
|
|
325
|
+
let isUserStoreInMemoryOnly: boolean;
|
|
325
326
|
|
|
326
327
|
const oidcClientTsUserManager = new OidcClientTsUserManager({
|
|
327
328
|
stateQueryParamValue: stateQueryParamValue_instance,
|
|
@@ -334,31 +335,51 @@ export async function createOidc_nonMemoized<
|
|
|
334
335
|
scope: Array.from(new Set(["openid", ...scopes])).join(" "),
|
|
335
336
|
automaticSilentRenew: false,
|
|
336
337
|
userStore: new WebStorageStateStore({
|
|
337
|
-
store:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
store: (() => {
|
|
339
|
+
if (areThirdPartyCookiesAllowed) {
|
|
340
|
+
isUserStoreInMemoryOnly = true;
|
|
341
|
+
return new InMemoryWebStorage();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
isUserStoreInMemoryOnly = false;
|
|
345
|
+
|
|
346
|
+
const storage = createEphemeralSessionStorage({
|
|
347
|
+
sessionStorageTtlMs: 3 * 60_000
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const { evtRequestToPersistTokens } = globalContext;
|
|
351
|
+
|
|
352
|
+
evtRequestToPersistTokens.subscribe(({ configIdOfInstancePostingTheRequest }) => {
|
|
353
|
+
if (configIdOfInstancePostingTheRequest === configId) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
storage.persistCurrentStateAndSubsequentChanges();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
return storage;
|
|
361
|
+
})()
|
|
342
362
|
}),
|
|
343
363
|
stateStore: new WebStorageStateStore({ store: localStorage, prefix: STATE_STORE_KEY_PREFIX }),
|
|
344
364
|
client_secret: __unsafe_clientSecret
|
|
345
365
|
});
|
|
346
366
|
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
toCallBeforeReturningOidcNotLoggedIn
|
|
351
|
-
} = createLoginOrGoToAuthServer({
|
|
367
|
+
const evtIsUserLoggedIn = createEvt<boolean>();
|
|
368
|
+
|
|
369
|
+
const { loginOrGoToAuthServer } = createLoginOrGoToAuthServer({
|
|
352
370
|
configId,
|
|
353
371
|
oidcClientTsUserManager,
|
|
354
372
|
getExtraQueryParams,
|
|
355
373
|
transformUrlBeforeRedirect,
|
|
356
374
|
homeAndCallbackUrl,
|
|
375
|
+
evtIsUserLoggedIn,
|
|
357
376
|
log
|
|
358
377
|
});
|
|
359
378
|
|
|
360
379
|
const BROWSER_SESSION_NOT_FIRST_INIT_KEY = `oidc-spa.browser-session-not-first-init:${configId}`;
|
|
361
380
|
|
|
381
|
+
const { completeLoginOrRefreshProcess } = await startLoginOrRefreshProcess();
|
|
382
|
+
|
|
362
383
|
const resultOfLoginProcess = await (async (): Promise<
|
|
363
384
|
| undefined // User is currently not logged in
|
|
364
385
|
| Error // Initialization error
|
|
@@ -368,58 +389,13 @@ export async function createOidc_nonMemoized<
|
|
|
368
389
|
}
|
|
369
390
|
> => {
|
|
370
391
|
handle_redirect_auth_response: {
|
|
371
|
-
const
|
|
372
|
-
const value = sessionStorage.getItem(AUTH_RESPONSE_KEY);
|
|
373
|
-
|
|
374
|
-
if (value === null) {
|
|
375
|
-
return undefined;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
let authResponse: unknown;
|
|
379
|
-
|
|
380
|
-
try {
|
|
381
|
-
authResponse = JSON.parse(value);
|
|
382
|
-
|
|
383
|
-
assert(
|
|
384
|
-
typeGuard<{ state: string; [key: string]: string }>(
|
|
385
|
-
authResponse,
|
|
386
|
-
authResponse instanceof Object &&
|
|
387
|
-
Object.values(authResponse).every(value => typeof value === "string")
|
|
388
|
-
),
|
|
389
|
-
"Valid json but not expected shape"
|
|
390
|
-
);
|
|
391
|
-
} catch (error) {
|
|
392
|
-
console.error(`Failed to parse auth response from callback URL ${String(error)}`);
|
|
393
|
-
return undefined;
|
|
394
|
-
}
|
|
392
|
+
const authResponseAndStateData = retrieveRedirectAuthResponseAndStateData({ configId });
|
|
395
393
|
|
|
396
|
-
|
|
397
|
-
})();
|
|
398
|
-
|
|
399
|
-
if (authResponse === undefined) {
|
|
394
|
+
if (authResponseAndStateData === undefined) {
|
|
400
395
|
break handle_redirect_auth_response;
|
|
401
396
|
}
|
|
402
397
|
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
assert(stateData !== undefined);
|
|
406
|
-
assert(stateData.context === "redirect");
|
|
407
|
-
|
|
408
|
-
const { evtAuthResponseHandled } = globalContext;
|
|
409
|
-
|
|
410
|
-
if (stateData.configId !== configId) {
|
|
411
|
-
// NOTE: Best attempt at letting the other client handle the request synchronously
|
|
412
|
-
// but we won't wait for it because the initialization of the other client might
|
|
413
|
-
// be contingent on the initialization of this client.
|
|
414
|
-
const prHandled = evtAuthResponseHandled.waitFor();
|
|
415
|
-
await Promise.resolve();
|
|
416
|
-
if (sessionStorage.getItem(AUTH_RESPONSE_KEY) === null) {
|
|
417
|
-
await prHandled;
|
|
418
|
-
}
|
|
419
|
-
break handle_redirect_auth_response;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
sessionStorage.removeItem(AUTH_RESPONSE_KEY);
|
|
398
|
+
const { authResponse, stateData } = authResponseAndStateData;
|
|
423
399
|
|
|
424
400
|
switch (stateData.action) {
|
|
425
401
|
case "login":
|
|
@@ -431,11 +407,9 @@ export async function createOidc_nonMemoized<
|
|
|
431
407
|
let oidcClientTsUser: OidcClientTsUser | undefined = undefined;
|
|
432
408
|
|
|
433
409
|
try {
|
|
434
|
-
oidcClientTsUser = await oidcClientTsUserManager
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
evtAuthResponseHandled.post();
|
|
438
|
-
});
|
|
410
|
+
oidcClientTsUser = await oidcClientTsUserManager.signinRedirectCallback(
|
|
411
|
+
authResponseUrl
|
|
412
|
+
);
|
|
439
413
|
} catch (error) {
|
|
440
414
|
assert(error instanceof Error);
|
|
441
415
|
|
|
@@ -447,11 +421,11 @@ export async function createOidc_nonMemoized<
|
|
|
447
421
|
}
|
|
448
422
|
|
|
449
423
|
{
|
|
450
|
-
const
|
|
424
|
+
const authResponse_error = authResponse.error;
|
|
451
425
|
|
|
452
|
-
if (
|
|
426
|
+
if (authResponse_error !== undefined) {
|
|
453
427
|
log?.(
|
|
454
|
-
`The auth server responded with: ${
|
|
428
|
+
`The auth server responded with: ${authResponse_error}, trying to restore from the http only cookie`
|
|
455
429
|
);
|
|
456
430
|
break handle_redirect_auth_response;
|
|
457
431
|
}
|
|
@@ -462,18 +436,31 @@ export async function createOidc_nonMemoized<
|
|
|
462
436
|
|
|
463
437
|
sessionStorage.removeItem(BROWSER_SESSION_NOT_FIRST_INIT_KEY);
|
|
464
438
|
|
|
439
|
+
notifyOtherTabsOfLogin({ configId });
|
|
440
|
+
|
|
465
441
|
return {
|
|
466
442
|
oidcClientTsUser,
|
|
467
443
|
backFromAuthServer: {
|
|
468
444
|
extraQueryParams: stateData.extraQueryParams,
|
|
469
445
|
result: Object.fromEntries(
|
|
470
|
-
Object.entries(authResponse)
|
|
471
|
-
([name]) =>
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
446
|
+
Object.entries(authResponse)
|
|
447
|
+
.map(([name, value]) => {
|
|
448
|
+
if (
|
|
449
|
+
name === "state" ||
|
|
450
|
+
name === "session_state" ||
|
|
451
|
+
name === "iss" ||
|
|
452
|
+
name === "code"
|
|
453
|
+
) {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (value === undefined) {
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return [name, value];
|
|
462
|
+
})
|
|
463
|
+
.filter(entry => entry !== undefined)
|
|
477
464
|
)
|
|
478
465
|
}
|
|
479
466
|
};
|
|
@@ -489,8 +476,6 @@ export async function createOidc_nonMemoized<
|
|
|
489
476
|
await oidcClientTsUserManager.signoutRedirectCallback(authResponseUrl);
|
|
490
477
|
} catch {}
|
|
491
478
|
|
|
492
|
-
evtAuthResponseHandled.post();
|
|
493
|
-
|
|
494
479
|
notifyOtherTabsOfLogout({
|
|
495
480
|
configId,
|
|
496
481
|
redirectUrl: stateData.redirectUrl,
|
|
@@ -505,7 +490,7 @@ export async function createOidc_nonMemoized<
|
|
|
505
490
|
}
|
|
506
491
|
|
|
507
492
|
restore_from_session_storage: {
|
|
508
|
-
if (
|
|
493
|
+
if (isUserStoreInMemoryOnly) {
|
|
509
494
|
break restore_from_session_storage;
|
|
510
495
|
}
|
|
511
496
|
|
|
@@ -575,7 +560,7 @@ export async function createOidc_nonMemoized<
|
|
|
575
560
|
|
|
576
561
|
log?.("Silent signin auth response", authResponse);
|
|
577
562
|
|
|
578
|
-
const authResponse_error
|
|
563
|
+
const authResponse_error = authResponse.error;
|
|
579
564
|
let oidcClientTsUser: OidcClientTsUser | undefined = undefined;
|
|
580
565
|
|
|
581
566
|
try {
|
|
@@ -608,6 +593,18 @@ export async function createOidc_nonMemoized<
|
|
|
608
593
|
) {
|
|
609
594
|
persistAuthState({ configId, state: undefined });
|
|
610
595
|
|
|
596
|
+
completeLoginOrRefreshProcess();
|
|
597
|
+
|
|
598
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
599
|
+
prUnlock: new Promise<never>(() => {})
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
if (persistedAuthState === "logged in") {
|
|
603
|
+
globalContext.evtRequestToPersistTokens.post({
|
|
604
|
+
configIdOfInstancePostingTheRequest: configId
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
611
608
|
await loginOrGoToAuthServer({
|
|
612
609
|
action: "login",
|
|
613
610
|
doForceReloadOnBfCache: true,
|
|
@@ -617,8 +614,7 @@ export async function createOidc_nonMemoized<
|
|
|
617
614
|
transformUrlBeforeRedirect_local: undefined,
|
|
618
615
|
doForceInteraction: false
|
|
619
616
|
});
|
|
620
|
-
|
|
621
|
-
// NOTE: Never here
|
|
617
|
+
assert(false);
|
|
622
618
|
}
|
|
623
619
|
|
|
624
620
|
log?.(
|
|
@@ -643,71 +639,15 @@ export async function createOidc_nonMemoized<
|
|
|
643
639
|
|
|
644
640
|
// NOTE: The user is not logged in.
|
|
645
641
|
return undefined;
|
|
646
|
-
})()
|
|
647
|
-
if (result === undefined) {
|
|
648
|
-
return undefined;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
if (result instanceof Error) {
|
|
652
|
-
return result;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
const { oidcClientTsUser, backFromAuthServer } = result;
|
|
656
|
-
|
|
657
|
-
log_real_decoded_id_token: {
|
|
658
|
-
if (log === undefined) {
|
|
659
|
-
break log_real_decoded_id_token;
|
|
660
|
-
}
|
|
661
|
-
const idToken = oidcClientTsUser.id_token;
|
|
662
|
-
|
|
663
|
-
if (idToken === undefined) {
|
|
664
|
-
break log_real_decoded_id_token;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
const decodedIdToken = decodeJwt(idToken);
|
|
668
|
-
|
|
669
|
-
log(
|
|
670
|
-
[
|
|
671
|
-
`Decoded ID token`,
|
|
672
|
-
decodedIdTokenSchema === undefined ? "" : " before `decodedIdTokenSchema.parse()`\n",
|
|
673
|
-
JSON.stringify(decodedIdToken, null, 2)
|
|
674
|
-
].join("")
|
|
675
|
-
);
|
|
676
|
-
|
|
677
|
-
if (decodedIdTokenSchema === undefined) {
|
|
678
|
-
break log_real_decoded_id_token;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
log(
|
|
682
|
-
[
|
|
683
|
-
"Decoded ID token after `decodedIdTokenSchema.parse()`\n",
|
|
684
|
-
JSON.stringify(decodedIdTokenSchema.parse(decodedIdToken), null, 2)
|
|
685
|
-
].join("")
|
|
686
|
-
);
|
|
687
|
-
}
|
|
642
|
+
})();
|
|
688
643
|
|
|
689
|
-
|
|
690
|
-
oidcClientTsUser,
|
|
691
|
-
decodedIdTokenSchema,
|
|
692
|
-
__unsafe_useIdTokenAsAccessToken,
|
|
693
|
-
decodedIdToken_previous: undefined,
|
|
694
|
-
log
|
|
695
|
-
});
|
|
644
|
+
completeLoginOrRefreshProcess();
|
|
696
645
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
[
|
|
700
|
-
"The OIDC refresh token shorter than the one of the access token.",
|
|
701
|
-
"This is very unusual and probably a misconfiguration.",
|
|
702
|
-
`Check your oidc server configuration for ${clientId} ${issuerUri}`
|
|
703
|
-
].join(" ")
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
return { tokens, backFromAuthServer };
|
|
646
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
647
|
+
prUnlock: Promise.resolve()
|
|
708
648
|
});
|
|
709
649
|
|
|
710
|
-
const
|
|
650
|
+
const oidc_common: Oidc.Common = {
|
|
711
651
|
params: {
|
|
712
652
|
issuerUri,
|
|
713
653
|
clientId
|
|
@@ -719,6 +659,12 @@ export async function createOidc_nonMemoized<
|
|
|
719
659
|
break not_loggedIn_case;
|
|
720
660
|
}
|
|
721
661
|
|
|
662
|
+
evtIsUserLoggedIn.post(false);
|
|
663
|
+
|
|
664
|
+
if (getPersistedAuthState({ configId }) !== "explicitly logged out") {
|
|
665
|
+
persistAuthState({ configId, state: undefined });
|
|
666
|
+
}
|
|
667
|
+
|
|
722
668
|
const oidc_notLoggedIn: Oidc.NotLoggedIn = (() => {
|
|
723
669
|
if (resultOfLoginProcess instanceof Error) {
|
|
724
670
|
log?.("User not logged in and there was an initialization error");
|
|
@@ -747,7 +693,7 @@ export async function createOidc_nonMemoized<
|
|
|
747
693
|
);
|
|
748
694
|
|
|
749
695
|
return id<Oidc.NotLoggedIn>({
|
|
750
|
-
...
|
|
696
|
+
...oidc_common,
|
|
751
697
|
isUserLoggedIn: false,
|
|
752
698
|
login: async () => {
|
|
753
699
|
alert("Authentication is currently unavailable. Please try again later.");
|
|
@@ -761,15 +707,19 @@ export async function createOidc_nonMemoized<
|
|
|
761
707
|
log?.("User not logged in");
|
|
762
708
|
|
|
763
709
|
return id<Oidc.NotLoggedIn>({
|
|
764
|
-
...
|
|
710
|
+
...oidc_common,
|
|
765
711
|
isUserLoggedIn: false,
|
|
766
|
-
login: ({
|
|
712
|
+
login: async ({
|
|
767
713
|
doesCurrentHrefRequiresAuth,
|
|
768
714
|
extraQueryParams,
|
|
769
715
|
redirectUrl,
|
|
770
716
|
transformUrlBeforeRedirect
|
|
771
|
-
}) =>
|
|
772
|
-
|
|
717
|
+
}) => {
|
|
718
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
719
|
+
prUnlock: getPrSafelyRestoredFromBfCacheAfterLoginBackNavigation()
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
return loginOrGoToAuthServer({
|
|
773
723
|
action: "login",
|
|
774
724
|
doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack:
|
|
775
725
|
doesCurrentHrefRequiresAuth,
|
|
@@ -780,7 +730,8 @@ export async function createOidc_nonMemoized<
|
|
|
780
730
|
transformUrlBeforeRedirect_local: transformUrlBeforeRedirect,
|
|
781
731
|
doForceInteraction:
|
|
782
732
|
getPersistedAuthState({ configId }) === "explicitly logged out"
|
|
783
|
-
})
|
|
733
|
+
});
|
|
734
|
+
},
|
|
784
735
|
initializationError: undefined
|
|
785
736
|
});
|
|
786
737
|
}
|
|
@@ -788,11 +739,21 @@ export async function createOidc_nonMemoized<
|
|
|
788
739
|
assert<Equals<typeof resultOfLoginProcess, never>>(false);
|
|
789
740
|
})();
|
|
790
741
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
742
|
+
{
|
|
743
|
+
const { prOtherTabLogin } = getPrOtherTabLogin({
|
|
744
|
+
configId
|
|
745
|
+
});
|
|
794
746
|
|
|
795
|
-
|
|
747
|
+
prOtherTabLogin.then(async () => {
|
|
748
|
+
log?.(`Other tab has logged in, reloading this tab`);
|
|
749
|
+
|
|
750
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
751
|
+
prUnlock: new Promise<never>(() => {})
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
window.location.reload();
|
|
755
|
+
});
|
|
756
|
+
}
|
|
796
757
|
|
|
797
758
|
// @ts-expect-error: We know what we're doing
|
|
798
759
|
return oidc_notLoggedIn;
|
|
@@ -800,7 +761,31 @@ export async function createOidc_nonMemoized<
|
|
|
800
761
|
|
|
801
762
|
log?.("User is logged in");
|
|
802
763
|
|
|
803
|
-
|
|
764
|
+
evtIsUserLoggedIn.post(true);
|
|
765
|
+
|
|
766
|
+
let currentTokens = oidcClientTsUserToTokens({
|
|
767
|
+
oidcClientTsUser: resultOfLoginProcess.oidcClientTsUser,
|
|
768
|
+
decodedIdTokenSchema,
|
|
769
|
+
__unsafe_useIdTokenAsAccessToken,
|
|
770
|
+
decodedIdToken_previous: undefined,
|
|
771
|
+
log
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
{
|
|
775
|
+
if (getPersistedAuthState({ configId }) !== undefined) {
|
|
776
|
+
persistAuthState({ configId, state: undefined });
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (!areThirdPartyCookiesAllowed) {
|
|
780
|
+
persistAuthState({
|
|
781
|
+
configId,
|
|
782
|
+
state: {
|
|
783
|
+
stateDescription: "logged in",
|
|
784
|
+
untilTime: currentTokens.refreshTokenExpirationTime
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
804
789
|
|
|
805
790
|
const autoLogoutCountdownTickCallbacks = new Set<
|
|
806
791
|
(params: { secondsLeft: number | undefined }) => void
|
|
@@ -809,7 +794,7 @@ export async function createOidc_nonMemoized<
|
|
|
809
794
|
const onTokenChanges = new Set<(tokens: Oidc.Tokens<DecodedIdToken>) => void>();
|
|
810
795
|
|
|
811
796
|
const oidc_loggedIn = id<Oidc.LoggedIn<DecodedIdToken>>({
|
|
812
|
-
...
|
|
797
|
+
...oidc_common,
|
|
813
798
|
isUserLoggedIn: true,
|
|
814
799
|
getTokens: () => currentTokens,
|
|
815
800
|
getTokens_next: async () => {
|
|
@@ -828,12 +813,6 @@ export async function createOidc_nonMemoized<
|
|
|
828
813
|
|
|
829
814
|
globalContext.hasLogoutBeenCalled = true;
|
|
830
815
|
|
|
831
|
-
document.addEventListener("visibilitychange", () => {
|
|
832
|
-
if (document.visibilityState === "visible") {
|
|
833
|
-
location.reload();
|
|
834
|
-
}
|
|
835
|
-
});
|
|
836
|
-
|
|
837
816
|
const postLogoutRedirectUrl: string = (() => {
|
|
838
817
|
switch (params.redirectTo) {
|
|
839
818
|
case "current page":
|
|
@@ -848,6 +827,14 @@ export async function createOidc_nonMemoized<
|
|
|
848
827
|
}
|
|
849
828
|
})();
|
|
850
829
|
|
|
830
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
831
|
+
prUnlock: new Promise<never>(() => {})
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
window.addEventListener("pageshow", () => {
|
|
835
|
+
location.reload();
|
|
836
|
+
});
|
|
837
|
+
|
|
851
838
|
try {
|
|
852
839
|
await oidcClientTsUserManager.signoutRedirect({
|
|
853
840
|
state: id<StateData>({
|
|
@@ -866,7 +853,7 @@ export async function createOidc_nonMemoized<
|
|
|
866
853
|
if (error.message === "No end session endpoint") {
|
|
867
854
|
log?.("No end session endpoint, managing logging state locally");
|
|
868
855
|
|
|
869
|
-
persistAuthState({ configId, state: "explicitly logged out" });
|
|
856
|
+
persistAuthState({ configId, state: { stateDescription: "explicitly logged out" } });
|
|
870
857
|
|
|
871
858
|
try {
|
|
872
859
|
await oidcClientTsUserManager.removeUser();
|
|
@@ -874,6 +861,12 @@ export async function createOidc_nonMemoized<
|
|
|
874
861
|
// NOTE: Not sure if it can throw
|
|
875
862
|
}
|
|
876
863
|
|
|
864
|
+
notifyOtherTabsOfLogout({
|
|
865
|
+
configId,
|
|
866
|
+
redirectUrl: postLogoutRedirectUrl,
|
|
867
|
+
sessionId
|
|
868
|
+
});
|
|
869
|
+
|
|
877
870
|
window.location.href = postLogoutRedirectUrl;
|
|
878
871
|
} else {
|
|
879
872
|
throw error;
|
|
@@ -888,6 +881,8 @@ export async function createOidc_nonMemoized<
|
|
|
888
881
|
|
|
889
882
|
log?.("Renewing tokens");
|
|
890
883
|
|
|
884
|
+
const { completeLoginOrRefreshProcess } = await startLoginOrRefreshProcess();
|
|
885
|
+
|
|
891
886
|
const result_loginSilent = await loginSilent({
|
|
892
887
|
oidcClientTsUserManager,
|
|
893
888
|
stateQueryParamValue_instance,
|
|
@@ -896,6 +891,7 @@ export async function createOidc_nonMemoized<
|
|
|
896
891
|
});
|
|
897
892
|
|
|
898
893
|
if (result_loginSilent.outcome === "failure") {
|
|
894
|
+
completeLoginOrRefreshProcess();
|
|
899
895
|
throw new Error(result_loginSilent.cause);
|
|
900
896
|
}
|
|
901
897
|
|
|
@@ -914,7 +910,7 @@ export async function createOidc_nonMemoized<
|
|
|
914
910
|
|
|
915
911
|
log?.("Tokens refresh using iframe", authResponse);
|
|
916
912
|
|
|
917
|
-
const authResponse_error
|
|
913
|
+
const authResponse_error = authResponse.error;
|
|
918
914
|
|
|
919
915
|
let oidcClientTsUser_scope: OidcClientTsUser | undefined = undefined;
|
|
920
916
|
|
|
@@ -927,6 +923,7 @@ export async function createOidc_nonMemoized<
|
|
|
927
923
|
assert(error instanceof Error);
|
|
928
924
|
|
|
929
925
|
if (authResponse_error === undefined) {
|
|
926
|
+
completeLoginOrRefreshProcess();
|
|
930
927
|
throw error;
|
|
931
928
|
}
|
|
932
929
|
|
|
@@ -936,6 +933,16 @@ export async function createOidc_nonMemoized<
|
|
|
936
933
|
if (oidcClientTsUser_scope === undefined) {
|
|
937
934
|
persistAuthState({ configId, state: undefined });
|
|
938
935
|
|
|
936
|
+
completeLoginOrRefreshProcess();
|
|
937
|
+
|
|
938
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
939
|
+
prUnlock: new Promise<never>(() => {})
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
globalContext.evtRequestToPersistTokens.post({
|
|
943
|
+
configIdOfInstancePostingTheRequest: configId
|
|
944
|
+
});
|
|
945
|
+
|
|
939
946
|
await loginOrGoToAuthServer({
|
|
940
947
|
action: "login",
|
|
941
948
|
redirectUrl: window.location.href,
|
|
@@ -964,7 +971,19 @@ export async function createOidc_nonMemoized<
|
|
|
964
971
|
log
|
|
965
972
|
});
|
|
966
973
|
|
|
974
|
+
if (getPersistedAuthState({ configId }) !== undefined) {
|
|
975
|
+
persistAuthState({
|
|
976
|
+
configId,
|
|
977
|
+
state: {
|
|
978
|
+
stateDescription: "logged in",
|
|
979
|
+
untilTime: currentTokens.refreshTokenExpirationTime
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
967
984
|
Array.from(onTokenChanges).forEach(onTokenChange => onTokenChange(currentTokens));
|
|
985
|
+
|
|
986
|
+
completeLoginOrRefreshProcess();
|
|
968
987
|
}
|
|
969
988
|
|
|
970
989
|
let ongoingCall:
|
|
@@ -1079,14 +1098,27 @@ export async function createOidc_nonMemoized<
|
|
|
1079
1098
|
sessionId
|
|
1080
1099
|
});
|
|
1081
1100
|
|
|
1082
|
-
prOtherTabLogout.then(({ redirectUrl }) => {
|
|
1101
|
+
prOtherTabLogout.then(async ({ redirectUrl }) => {
|
|
1083
1102
|
log?.(`Other tab has logged out, redirecting to ${redirectUrl}`);
|
|
1103
|
+
|
|
1104
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
1105
|
+
prUnlock: new Promise<never>(() => {})
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
window.addEventListener("pageshow", () => {
|
|
1109
|
+
location.reload();
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1084
1112
|
window.location.href = redirectUrl;
|
|
1085
1113
|
});
|
|
1086
1114
|
}
|
|
1087
1115
|
|
|
1088
1116
|
(function scheduleRenew() {
|
|
1089
|
-
const login_dueToExpiration = () => {
|
|
1117
|
+
const login_dueToExpiration = async () => {
|
|
1118
|
+
await waitForAllOtherOngoingLoginOrRefreshProcessesToComplete({
|
|
1119
|
+
prUnlock: new Promise<never>(() => {})
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1090
1122
|
persistAuthState({ configId, state: undefined });
|
|
1091
1123
|
|
|
1092
1124
|
return loginOrGoToAuthServer({
|
|
@@ -1149,9 +1181,16 @@ export async function createOidc_nonMemoized<
|
|
|
1149
1181
|
})();
|
|
1150
1182
|
|
|
1151
1183
|
auto_logout: {
|
|
1152
|
-
if (
|
|
1184
|
+
if (
|
|
1185
|
+
(!currentTokens.hasRefreshToken || currentTokens.refreshTokenExpirationTime === undefined) &&
|
|
1186
|
+
__unsafe_ssoSessionIdleSeconds === undefined
|
|
1187
|
+
) {
|
|
1153
1188
|
log?.(
|
|
1154
|
-
|
|
1189
|
+
`${
|
|
1190
|
+
currentTokens.hasRefreshToken
|
|
1191
|
+
? "The refresh token is opaque, we can't read it's expiration time"
|
|
1192
|
+
: "No refresh token"
|
|
1193
|
+
}, and __unsafe_ssoSessionIdleSeconds was not set, can't implement auto logout mechanism`
|
|
1155
1194
|
);
|
|
1156
1195
|
break auto_logout;
|
|
1157
1196
|
}
|
|
@@ -1161,7 +1200,9 @@ export async function createOidc_nonMemoized<
|
|
|
1161
1200
|
const getCountdownEndTime = () =>
|
|
1162
1201
|
__unsafe_ssoSessionIdleSeconds !== undefined
|
|
1163
1202
|
? Date.now() + __unsafe_ssoSessionIdleSeconds * 1000
|
|
1164
|
-
: currentTokens.
|
|
1203
|
+
: (assert(currentTokens.hasRefreshToken),
|
|
1204
|
+
assert(currentTokens.refreshTokenExpirationTime !== undefined),
|
|
1205
|
+
currentTokens.refreshTokenExpirationTime);
|
|
1165
1206
|
|
|
1166
1207
|
const durationBeforeAutoLogout = toHumanReadableDuration(
|
|
1167
1208
|
getCountdownEndTime() - Date.now()
|
|
@@ -1193,14 +1234,12 @@ export async function createOidc_nonMemoized<
|
|
|
1193
1234
|
|
|
1194
1235
|
let stopCountdown: (() => void) | undefined = undefined;
|
|
1195
1236
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
});
|
|
1201
|
-
}
|
|
1237
|
+
const evtIsUserActive = createEvtIsUserActive({
|
|
1238
|
+
configId,
|
|
1239
|
+
sessionId
|
|
1240
|
+
});
|
|
1202
1241
|
|
|
1203
|
-
|
|
1242
|
+
evtIsUserActive.subscribe(isUserActive => {
|
|
1204
1243
|
if (isUserActive) {
|
|
1205
1244
|
if (stopCountdown !== undefined) {
|
|
1206
1245
|
stopCountdown();
|
|
@@ -1213,17 +1252,5 @@ export async function createOidc_nonMemoized<
|
|
|
1213
1252
|
});
|
|
1214
1253
|
}
|
|
1215
1254
|
|
|
1216
|
-
{
|
|
1217
|
-
if (getPersistedAuthState({ configId }) !== undefined) {
|
|
1218
|
-
persistAuthState({ configId, state: undefined });
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
if (!areThirdPartyCookiesAllowed) {
|
|
1222
|
-
persistAuthState({ configId, state: "logged in" });
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
toCallBeforeReturningOidcLoggedIn();
|
|
1227
|
-
|
|
1228
1255
|
return oidc_loggedIn;
|
|
1229
1256
|
}
|