oidc-spa 8.2.1 → 8.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/AuthResponse.d.ts +0 -5
- package/core/AuthResponse.js +0 -25
- package/core/AuthResponse.js.map +1 -1
- package/core/createOidc.d.ts +49 -16
- package/core/createOidc.js +97 -122
- package/core/createOidc.js.map +1 -1
- package/core/desiredPostLoginRedirectUrl.d.ts +4 -0
- package/core/desiredPostLoginRedirectUrl.js +12 -0
- package/core/desiredPostLoginRedirectUrl.js.map +1 -0
- package/core/diagnostic.d.ts +1 -1
- package/core/diagnostic.js +3 -3
- package/core/diagnostic.js.map +1 -1
- package/core/instancesThatCantUseIframes.d.ts +2 -0
- package/core/instancesThatCantUseIframes.js +20 -0
- package/core/instancesThatCantUseIframes.js.map +1 -0
- package/core/loginOrGoToAuthServer.d.ts +1 -0
- package/core/loginOrGoToAuthServer.js +3 -0
- package/core/loginOrGoToAuthServer.js.map +1 -1
- package/core/persistedAuthState.d.ts +1 -0
- package/core/persistedAuthState.js +14 -4
- package/core/persistedAuthState.js.map +1 -1
- package/esm/angular.d.ts +27 -4
- package/esm/angular.js +28 -6
- package/esm/angular.js.map +1 -1
- package/esm/core/AuthResponse.d.ts +0 -5
- package/esm/core/AuthResponse.js +0 -23
- package/esm/core/AuthResponse.js.map +1 -1
- package/esm/core/createOidc.d.ts +49 -16
- package/esm/core/createOidc.js +98 -123
- package/esm/core/createOidc.js.map +1 -1
- package/esm/core/desiredPostLoginRedirectUrl.d.ts +4 -0
- package/esm/core/desiredPostLoginRedirectUrl.js +8 -0
- package/esm/core/desiredPostLoginRedirectUrl.js.map +1 -0
- package/esm/core/diagnostic.d.ts +1 -1
- package/esm/core/diagnostic.js +3 -3
- package/esm/core/diagnostic.js.map +1 -1
- package/esm/core/instancesThatCantUseIframes.d.ts +2 -0
- package/esm/core/instancesThatCantUseIframes.js +16 -0
- package/esm/core/instancesThatCantUseIframes.js.map +1 -0
- package/esm/core/loginOrGoToAuthServer.d.ts +1 -0
- package/esm/core/loginOrGoToAuthServer.js +3 -0
- package/esm/core/loginOrGoToAuthServer.js.map +1 -1
- package/esm/core/persistedAuthState.d.ts +1 -0
- package/esm/core/persistedAuthState.js +14 -4
- package/esm/core/persistedAuthState.js.map +1 -1
- package/esm/keycloak/keycloak-js/Keycloak.d.ts +40 -0
- package/esm/keycloak/keycloak-js/Keycloak.js +2 -1
- package/esm/keycloak/keycloak-js/Keycloak.js.map +1 -1
- package/esm/react/react.js +24 -2
- package/esm/react/react.js.map +1 -1
- package/esm/react-spa/createOidcSpaApi.js +26 -4
- package/esm/react-spa/createOidcSpaApi.js.map +1 -1
- package/esm/react-spa/types.d.ts +26 -3
- package/esm/tanstack-start/react/createOidcSpaApi.js +25 -3
- package/esm/tanstack-start/react/createOidcSpaApi.js.map +1 -1
- package/esm/tanstack-start/react/types.d.ts +26 -3
- package/esm/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +4 -4
- package/esm/tools/lazySessionStorage.js +83 -0
- package/esm/tools/lazySessionStorage.js.map +1 -0
- package/keycloak/keycloak-js/Keycloak.d.ts +40 -0
- package/keycloak/keycloak-js/Keycloak.js +2 -1
- package/keycloak/keycloak-js/Keycloak.js.map +1 -1
- package/package.json +5 -1
- package/react/react.js +24 -2
- package/react/react.js.map +1 -1
- package/react-spa/createOidcSpaApi.js +26 -4
- package/react-spa/createOidcSpaApi.js.map +1 -1
- package/react-spa/types.d.ts +26 -3
- package/src/angular.ts +72 -18
- package/src/core/AuthResponse.ts +0 -36
- package/src/core/createOidc.ts +160 -173
- package/src/core/desiredPostLoginRedirectUrl.ts +9 -0
- package/src/core/diagnostic.ts +4 -4
- package/src/core/instancesThatCantUseIframes.ts +24 -0
- package/src/core/loginOrGoToAuthServer.ts +5 -0
- package/src/core/persistedAuthState.ts +27 -5
- package/src/keycloak/keycloak-js/Keycloak.ts +43 -1
- package/src/react/react.tsx +32 -3
- package/src/react-spa/createOidcSpaApi.tsx +34 -5
- package/src/react-spa/types.tsx +26 -3
- package/src/tanstack-start/react/createOidcSpaApi.tsx +33 -4
- package/src/tanstack-start/react/types.tsx +26 -3
- package/src/tools/lazySessionStorage.ts +123 -0
- package/src/vite-plugin/manageOptimizedDeps.ts +4 -1
- package/tools/{EphemeralSessionStorage.d.ts → lazySessionStorage.d.ts} +4 -4
- package/tools/lazySessionStorage.js +86 -0
- package/tools/lazySessionStorage.js.map +1 -0
- package/vite-plugin/manageOptimizedDeps.js +3 -1
- package/vite-plugin/manageOptimizedDeps.js.map +1 -1
- package/esm/tools/EphemeralSessionStorage.js +0 -143
- package/esm/tools/EphemeralSessionStorage.js.map +0 -1
- package/src/tools/EphemeralSessionStorage.ts +0 -225
- package/tools/EphemeralSessionStorage.js +0 -146
- package/tools/EphemeralSessionStorage.js.map +0 -1
|
@@ -30,6 +30,7 @@ export function persistAuthState(params: {
|
|
|
30
30
|
stateDescription: "logged in";
|
|
31
31
|
idleSessionLifetimeInSeconds: number | undefined;
|
|
32
32
|
refreshTokenExpirationTime: number | undefined;
|
|
33
|
+
serverDateNow: number;
|
|
33
34
|
}
|
|
34
35
|
| {
|
|
35
36
|
stateDescription: "explicitly logged out";
|
|
@@ -56,14 +57,35 @@ export function persistAuthState(params: {
|
|
|
56
57
|
__brand: "PersistedAuthState-v1",
|
|
57
58
|
stateDescription: "logged in",
|
|
58
59
|
untilTime: (() => {
|
|
59
|
-
const {
|
|
60
|
-
|
|
60
|
+
const {
|
|
61
|
+
idleSessionLifetimeInSeconds,
|
|
62
|
+
refreshTokenExpirationTime,
|
|
63
|
+
serverDateNow
|
|
64
|
+
} = state;
|
|
65
|
+
|
|
66
|
+
const untilTime_real = (() => {
|
|
67
|
+
if (refreshTokenExpirationTime === undefined) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const msBeforeExpirationOfTheSession =
|
|
72
|
+
refreshTokenExpirationTime - serverDateNow;
|
|
73
|
+
|
|
74
|
+
return Date.now() + msBeforeExpirationOfTheSession;
|
|
75
|
+
})();
|
|
76
|
+
|
|
77
|
+
const unitTime_userOverwrite = (() => {
|
|
78
|
+
if (idleSessionLifetimeInSeconds === undefined) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
61
81
|
|
|
62
|
-
if (idleSessionLifetimeInSeconds !== undefined) {
|
|
63
82
|
return Date.now() + idleSessionLifetimeInSeconds * 1000;
|
|
64
|
-
}
|
|
83
|
+
})();
|
|
65
84
|
|
|
66
|
-
return
|
|
85
|
+
return Math.min(
|
|
86
|
+
untilTime_real ?? Number.POSITIVE_INFINITY,
|
|
87
|
+
unitTime_userOverwrite ?? Number.POSITIVE_INFINITY
|
|
88
|
+
);
|
|
67
89
|
})()
|
|
68
90
|
});
|
|
69
91
|
case "explicitly logged out":
|
|
@@ -23,7 +23,48 @@ import { type StatefulEvt, createStatefulEvt } from "../../tools/StatefulEvt";
|
|
|
23
23
|
import { readExpirationTimeInJwt } from "../../tools/readExpirationTimeInJwt";
|
|
24
24
|
|
|
25
25
|
type ConstructorParams = KeycloakServerConfig & {
|
|
26
|
+
/**
|
|
27
|
+
* NOTE: This parameter is optional if you use the Vite plugin.
|
|
28
|
+
*
|
|
29
|
+
* This parameter let's you overwrite the value provided in
|
|
30
|
+
* oidcEarlyInit({ BASE_URL: xxx });
|
|
31
|
+
*
|
|
32
|
+
* What should you put in this parameter?
|
|
33
|
+
* - Vite project: `BASE_URL: import.meta.env.BASE_URL`
|
|
34
|
+
* - Create React App project: `BASE_URL: process.env.PUBLIC_URL`
|
|
35
|
+
* - Other: `BASE_URL: "/"` (Usually, or `/dashboard` if your app is not at the root of the domain)
|
|
36
|
+
*/
|
|
26
37
|
BASE_URL?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Determines how session restoration is handled.
|
|
41
|
+
* Session restoration allows users to stay logged in between visits
|
|
42
|
+
* without needing to explicitly sign in each time.
|
|
43
|
+
*
|
|
44
|
+
* Options:
|
|
45
|
+
*
|
|
46
|
+
* - **"auto" (default)**:
|
|
47
|
+
* Automatically selects the best method.
|
|
48
|
+
* If the app’s domain shares a common parent domain with the authorization endpoint,
|
|
49
|
+
* an iframe is used for silent session restoration.
|
|
50
|
+
* Otherwise, a full-page redirect is used.
|
|
51
|
+
*
|
|
52
|
+
* - **"full page redirect"**:
|
|
53
|
+
* Forces full-page reloads for session restoration.
|
|
54
|
+
* Use this if your application is served with a restrictive CSP
|
|
55
|
+
* (e.g., `Content-Security-Policy: frame-ancestors "none"`)
|
|
56
|
+
* or `X-Frame-Options: DENY`, and you cannot modify those headers.
|
|
57
|
+
* This mode provides a slightly less seamless UX and will lead oidc-spa to
|
|
58
|
+
* store tokens in `localStorage` if multiple OIDC clients are used
|
|
59
|
+
* (e.g., your app communicates with several APIs).
|
|
60
|
+
*
|
|
61
|
+
* - **"iframe"**:
|
|
62
|
+
* Forces iframe-based session restoration.
|
|
63
|
+
* In development, if you go in your browser setting and allow your auth server’s domain
|
|
64
|
+
* to set third-party cookies this value will let you test your app
|
|
65
|
+
* with the local dev server as it will behave in production.
|
|
66
|
+
*/
|
|
67
|
+
sessionRestorationMethod?: "iframe" | "full page redirect" | "auto";
|
|
27
68
|
};
|
|
28
69
|
|
|
29
70
|
/**
|
|
@@ -99,7 +140,8 @@ export class Keycloak {
|
|
|
99
140
|
let hasCreateResolved = false;
|
|
100
141
|
|
|
101
142
|
const oidcOrError = await createOidc({
|
|
102
|
-
|
|
143
|
+
BASE_URL: constructorParams.BASE_URL,
|
|
144
|
+
sessionRestorationMethod: constructorParams.sessionRestorationMethod,
|
|
103
145
|
issuerUri,
|
|
104
146
|
clientId: this.#state.constructorParams.clientId,
|
|
105
147
|
autoLogin,
|
package/src/react/react.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import { id } from "../tools/tsafe/id";
|
|
|
15
15
|
import type { ValueOrAsyncGetter } from "../tools/ValueOrAsyncGetter";
|
|
16
16
|
import { Deferred } from "../tools/Deferred";
|
|
17
17
|
import { toFullyQualifiedUrl } from "../tools/toFullyQualifiedUrl";
|
|
18
|
+
import { setDesiredPostLoginRedirectUrl } from "../core/desiredPostLoginRedirectUrl";
|
|
18
19
|
|
|
19
20
|
export type OidcReact<DecodedIdToken extends Record<string, unknown>> =
|
|
20
21
|
| OidcReact.NotLoggedIn
|
|
@@ -409,20 +410,48 @@ export function createReactOidc_dependencyInjection<
|
|
|
409
410
|
|
|
410
411
|
const oidc = await getOidc();
|
|
411
412
|
|
|
413
|
+
const isUrlAlreadyReplaced =
|
|
414
|
+
window.location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
|
|
415
|
+
|
|
412
416
|
if (!oidc.isUserLoggedIn) {
|
|
413
417
|
if (cause === "preload") {
|
|
414
418
|
throw new Error(
|
|
415
419
|
"oidc-spa: User is not yet logged in. This is an expected error, nothing to be addressed."
|
|
416
420
|
);
|
|
417
421
|
}
|
|
418
|
-
const doesCurrentHrefRequiresAuth =
|
|
419
|
-
location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
|
|
420
422
|
|
|
421
423
|
await oidc.login({
|
|
422
424
|
redirectUrl,
|
|
423
|
-
doesCurrentHrefRequiresAuth
|
|
425
|
+
doesCurrentHrefRequiresAuth: isUrlAlreadyReplaced
|
|
424
426
|
});
|
|
425
427
|
}
|
|
428
|
+
|
|
429
|
+
define_temporary_postLoginRedirectUrl: {
|
|
430
|
+
if (isUrlAlreadyReplaced) {
|
|
431
|
+
break define_temporary_postLoginRedirectUrl;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
setDesiredPostLoginRedirectUrl({ postLoginRedirectUrl: redirectUrl });
|
|
435
|
+
|
|
436
|
+
const history_pushState = history.pushState;
|
|
437
|
+
const history_replaceState = history.replaceState;
|
|
438
|
+
|
|
439
|
+
const onNavigated = () => {
|
|
440
|
+
history.pushState = history_pushState;
|
|
441
|
+
history.replaceState = history_replaceState;
|
|
442
|
+
setDesiredPostLoginRedirectUrl({ postLoginRedirectUrl: undefined });
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
history.pushState = function pushState(...args) {
|
|
446
|
+
onNavigated();
|
|
447
|
+
return history_pushState.call(history, ...args);
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
history.replaceState = function replaceState(...args) {
|
|
451
|
+
onNavigated();
|
|
452
|
+
return history_replaceState.call(history, ...args);
|
|
453
|
+
};
|
|
454
|
+
}
|
|
426
455
|
}
|
|
427
456
|
|
|
428
457
|
async function getOidc(): Promise<Oidc<DecodedIdToken>> {
|
|
@@ -10,6 +10,7 @@ import { createObjectThatThrowsIfAccessed } from "../tools/createObjectThatThrow
|
|
|
10
10
|
import { createStatefulEvt } from "../tools/StatefulEvt";
|
|
11
11
|
import { id } from "../tools/tsafe/id";
|
|
12
12
|
import { toFullyQualifiedUrl } from "../tools/toFullyQualifiedUrl";
|
|
13
|
+
import { setDesiredPostLoginRedirectUrl } from "../core/desiredPostLoginRedirectUrl";
|
|
13
14
|
|
|
14
15
|
export function createOidcSpaApi<
|
|
15
16
|
AutoLogin extends boolean,
|
|
@@ -390,7 +391,7 @@ export function createOidcSpaApi<
|
|
|
390
391
|
transformUrlBeforeRedirect: paramsOfBootstrap.transformUrlBeforeRedirect,
|
|
391
392
|
extraQueryParams: paramsOfBootstrap.extraQueryParams,
|
|
392
393
|
extraTokenParams: paramsOfBootstrap.extraTokenParams,
|
|
393
|
-
|
|
394
|
+
sessionRestorationMethod: paramsOfBootstrap.sessionRestorationMethod,
|
|
394
395
|
debugLogs: paramsOfBootstrap.debugLogs,
|
|
395
396
|
__unsafe_clientSecret: paramsOfBootstrap.__unsafe_clientSecret,
|
|
396
397
|
__metadata: paramsOfBootstrap.__metadata,
|
|
@@ -440,25 +441,53 @@ export function createOidcSpaApi<
|
|
|
440
441
|
});
|
|
441
442
|
}
|
|
442
443
|
|
|
443
|
-
return location.href;
|
|
444
|
+
return window.location.href;
|
|
444
445
|
})();
|
|
445
446
|
|
|
446
447
|
const oidc = await getOidc();
|
|
447
448
|
|
|
449
|
+
const isUrlAlreadyReplaced =
|
|
450
|
+
window.location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
|
|
451
|
+
|
|
448
452
|
if (!oidc.isUserLoggedIn) {
|
|
449
453
|
if (cause === "preload") {
|
|
450
454
|
throw new Error(
|
|
451
455
|
"oidc-spa: User is not yet logged in. This is an expected error, nothing to be addressed."
|
|
452
456
|
);
|
|
453
457
|
}
|
|
454
|
-
const doesCurrentHrefRequiresAuth =
|
|
455
|
-
location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
|
|
456
458
|
|
|
457
459
|
await oidc.login({
|
|
458
460
|
redirectUrl,
|
|
459
|
-
doesCurrentHrefRequiresAuth
|
|
461
|
+
doesCurrentHrefRequiresAuth: isUrlAlreadyReplaced
|
|
460
462
|
});
|
|
461
463
|
}
|
|
464
|
+
|
|
465
|
+
define_temporary_postLoginRedirectUrl: {
|
|
466
|
+
if (isUrlAlreadyReplaced) {
|
|
467
|
+
break define_temporary_postLoginRedirectUrl;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
setDesiredPostLoginRedirectUrl({ postLoginRedirectUrl: redirectUrl });
|
|
471
|
+
|
|
472
|
+
const history_pushState = history.pushState;
|
|
473
|
+
const history_replaceState = history.replaceState;
|
|
474
|
+
|
|
475
|
+
const onNavigated = () => {
|
|
476
|
+
history.pushState = history_pushState;
|
|
477
|
+
history.replaceState = history_replaceState;
|
|
478
|
+
setDesiredPostLoginRedirectUrl({ postLoginRedirectUrl: undefined });
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
history.pushState = function pushState(...args) {
|
|
482
|
+
onNavigated();
|
|
483
|
+
return history_pushState.call(history, ...args);
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
history.replaceState = function replaceState(...args) {
|
|
487
|
+
onNavigated();
|
|
488
|
+
return history_replaceState.call(history, ...args);
|
|
489
|
+
};
|
|
490
|
+
}
|
|
462
491
|
}
|
|
463
492
|
|
|
464
493
|
function OidcInitializationErrorGate(props: {
|
package/src/react-spa/types.tsx
CHANGED
|
@@ -213,11 +213,34 @@ export namespace ParamsOfBootstrap {
|
|
|
213
213
|
| (() => Record<string, string | undefined>);
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
|
-
*
|
|
216
|
+
* Determines how session restoration is handled.
|
|
217
|
+
* Session restoration allows users to stay logged in between visits
|
|
218
|
+
* without needing to explicitly sign in each time.
|
|
217
219
|
*
|
|
218
|
-
*
|
|
220
|
+
* Options:
|
|
221
|
+
*
|
|
222
|
+
* - **"auto" (default)**:
|
|
223
|
+
* Automatically selects the best method.
|
|
224
|
+
* If the app’s domain shares a common parent domain with the authorization endpoint,
|
|
225
|
+
* an iframe is used for silent session restoration.
|
|
226
|
+
* Otherwise, a full-page redirect is used.
|
|
227
|
+
*
|
|
228
|
+
* - **"full page redirect"**:
|
|
229
|
+
* Forces full-page reloads for session restoration.
|
|
230
|
+
* Use this if your application is served with a restrictive CSP
|
|
231
|
+
* (e.g., `Content-Security-Policy: frame-ancestors "none"`)
|
|
232
|
+
* or `X-Frame-Options: DENY`, and you cannot modify those headers.
|
|
233
|
+
* This mode provides a slightly less seamless UX and will lead oidc-spa to
|
|
234
|
+
* store tokens in `localStorage` if multiple OIDC clients are used
|
|
235
|
+
* (e.g., your app communicates with several APIs).
|
|
236
|
+
*
|
|
237
|
+
* - **"iframe"**:
|
|
238
|
+
* Forces iframe-based session restoration.
|
|
239
|
+
* In development, if you go in your browser setting and allow your auth server’s domain
|
|
240
|
+
* to set third-party cookies this value will let you test your app
|
|
241
|
+
* with the local dev server as it will behave in production.
|
|
219
242
|
*/
|
|
220
|
-
|
|
243
|
+
sessionRestorationMethod?: "iframe" | "full page redirect" | "auto";
|
|
221
244
|
|
|
222
245
|
debugLogs?: boolean;
|
|
223
246
|
|
|
@@ -30,6 +30,7 @@ import { createServerFn, createMiddleware } from "@tanstack/react-start";
|
|
|
30
30
|
import { getRequest, setResponseHeader, setResponseStatus } from "@tanstack/react-start/server";
|
|
31
31
|
import { toFullyQualifiedUrl } from "../../tools/toFullyQualifiedUrl";
|
|
32
32
|
import { UnifiedClientRetryForSsrLoadersError } from "./rfcUnifiedClientRetryForSsrLoaders/UnifiedClientRetryForSsrLoadersError";
|
|
33
|
+
import { setDesiredPostLoginRedirectUrl } from "../../core/desiredPostLoginRedirectUrl";
|
|
33
34
|
|
|
34
35
|
export function createOidcSpaApi<
|
|
35
36
|
AutoLogin extends boolean,
|
|
@@ -662,7 +663,7 @@ export function createOidcSpaApi<
|
|
|
662
663
|
transformUrlBeforeRedirect: paramsOfBootstrap.transformUrlBeforeRedirect,
|
|
663
664
|
extraQueryParams: paramsOfBootstrap.extraQueryParams,
|
|
664
665
|
extraTokenParams: paramsOfBootstrap.extraTokenParams,
|
|
665
|
-
|
|
666
|
+
sessionRestorationMethod: paramsOfBootstrap.sessionRestorationMethod,
|
|
666
667
|
debugLogs: paramsOfBootstrap.debugLogs,
|
|
667
668
|
__unsafe_clientSecret: paramsOfBootstrap.__unsafe_clientSecret,
|
|
668
669
|
__metadata: paramsOfBootstrap.__metadata,
|
|
@@ -714,6 +715,9 @@ export function createOidcSpaApi<
|
|
|
714
715
|
|
|
715
716
|
const oidc = await getOidc();
|
|
716
717
|
|
|
718
|
+
const isUrlAlreadyReplaced =
|
|
719
|
+
window.location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
|
|
720
|
+
|
|
717
721
|
if (!oidc.isUserLoggedIn) {
|
|
718
722
|
if (cause === "preload") {
|
|
719
723
|
throw new Error(
|
|
@@ -724,14 +728,39 @@ export function createOidcSpaApi<
|
|
|
724
728
|
].join(" ")
|
|
725
729
|
);
|
|
726
730
|
}
|
|
727
|
-
const doesCurrentHrefRequiresAuth =
|
|
728
|
-
location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
|
|
729
731
|
|
|
730
732
|
await oidc.login({
|
|
731
733
|
redirectUrl,
|
|
732
|
-
doesCurrentHrefRequiresAuth
|
|
734
|
+
doesCurrentHrefRequiresAuth: isUrlAlreadyReplaced
|
|
733
735
|
});
|
|
734
736
|
}
|
|
737
|
+
|
|
738
|
+
define_temporary_postLoginRedirectUrl: {
|
|
739
|
+
if (isUrlAlreadyReplaced) {
|
|
740
|
+
break define_temporary_postLoginRedirectUrl;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
setDesiredPostLoginRedirectUrl({ postLoginRedirectUrl: redirectUrl });
|
|
744
|
+
|
|
745
|
+
const history_pushState = history.pushState;
|
|
746
|
+
const history_replaceState = history.replaceState;
|
|
747
|
+
|
|
748
|
+
const onNavigated = () => {
|
|
749
|
+
history.pushState = history_pushState;
|
|
750
|
+
history.replaceState = history_replaceState;
|
|
751
|
+
setDesiredPostLoginRedirectUrl({ postLoginRedirectUrl: undefined });
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
history.pushState = function pushState(...args) {
|
|
755
|
+
onNavigated();
|
|
756
|
+
return history_pushState.call(history, ...args);
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
history.replaceState = function replaceState(...args) {
|
|
760
|
+
onNavigated();
|
|
761
|
+
return history_replaceState.call(history, ...args);
|
|
762
|
+
};
|
|
763
|
+
}
|
|
735
764
|
}
|
|
736
765
|
|
|
737
766
|
enforceLogin.__isOidcSpaEnforceLogin = true;
|
|
@@ -306,11 +306,34 @@ export namespace ParamsOfBootstrap {
|
|
|
306
306
|
| (() => Record<string, string | undefined>);
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
|
-
*
|
|
309
|
+
* Determines how session restoration is handled.
|
|
310
|
+
* Session restoration allows users to stay logged in between visits
|
|
311
|
+
* without needing to explicitly sign in each time.
|
|
310
312
|
*
|
|
311
|
-
*
|
|
313
|
+
* Options:
|
|
314
|
+
*
|
|
315
|
+
* - **"auto" (default)**:
|
|
316
|
+
* Automatically selects the best method.
|
|
317
|
+
* If the app’s domain shares a common parent domain with the authorization endpoint,
|
|
318
|
+
* an iframe is used for silent session restoration.
|
|
319
|
+
* Otherwise, a full-page redirect is used.
|
|
320
|
+
*
|
|
321
|
+
* - **"full page redirect"**:
|
|
322
|
+
* Forces full-page reloads for session restoration.
|
|
323
|
+
* Use this if your application is served with a restrictive CSP
|
|
324
|
+
* (e.g., `Content-Security-Policy: frame-ancestors "none"`)
|
|
325
|
+
* or `X-Frame-Options: DENY`, and you cannot modify those headers.
|
|
326
|
+
* This mode provides a slightly less seamless UX and will lead oidc-spa to
|
|
327
|
+
* store tokens in `localStorage` if multiple OIDC clients are used
|
|
328
|
+
* (e.g., your app communicates with several APIs).
|
|
329
|
+
*
|
|
330
|
+
* - **"iframe"**:
|
|
331
|
+
* Forces iframe-based session restoration.
|
|
332
|
+
* In development, if you go in your browser setting and allow your auth server’s domain
|
|
333
|
+
* to set third-party cookies this value will let you test your app
|
|
334
|
+
* with the local dev server as it will behave in production.
|
|
312
335
|
*/
|
|
313
|
-
|
|
336
|
+
sessionRestorationMethod?: "iframe" | "full page redirect" | "auto";
|
|
314
337
|
|
|
315
338
|
debugLogs?: boolean;
|
|
316
339
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { assert } from "../tools/tsafe/assert";
|
|
2
|
+
|
|
3
|
+
export type LazySessionStorage = {
|
|
4
|
+
// `Storage` methods, we don't use the type directly because it has [name: string]: any;
|
|
5
|
+
readonly length: number;
|
|
6
|
+
clear(): void;
|
|
7
|
+
getItem(key: string): string | null;
|
|
8
|
+
key(index: number): string | null;
|
|
9
|
+
removeItem(key: string): void;
|
|
10
|
+
setItem(key: string, value: string): void;
|
|
11
|
+
|
|
12
|
+
// Custom method
|
|
13
|
+
persistCurrentStateAndSubsequentChanges: () => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function createLazySessionStorage(params: { storageId: string }): LazySessionStorage {
|
|
17
|
+
const { storageId } = params;
|
|
18
|
+
|
|
19
|
+
const sessionStoragePrefix = `lazy-session-storage:${storageId}:`;
|
|
20
|
+
|
|
21
|
+
const getSessionStorageKey = (key: string) => `${sessionStoragePrefix}${key}`;
|
|
22
|
+
|
|
23
|
+
const entries: { key: string; value: string }[] = [];
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
26
|
+
const key = sessionStorage.key(i);
|
|
27
|
+
assert(key !== null, "470498");
|
|
28
|
+
|
|
29
|
+
if (!key.startsWith(sessionStoragePrefix)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const value = sessionStorage.getItem(key);
|
|
34
|
+
|
|
35
|
+
assert(value !== null, "846771");
|
|
36
|
+
|
|
37
|
+
sessionStorage.removeItem(key);
|
|
38
|
+
|
|
39
|
+
entries.push({
|
|
40
|
+
key: key.slice(sessionStoragePrefix.length),
|
|
41
|
+
value
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let isPersistenceEnabled = false;
|
|
46
|
+
|
|
47
|
+
const storage: LazySessionStorage = {
|
|
48
|
+
persistCurrentStateAndSubsequentChanges: () => {
|
|
49
|
+
isPersistenceEnabled = true;
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < storage.length; i++) {
|
|
52
|
+
const key = storage.key(i);
|
|
53
|
+
assert(key !== null, "803385");
|
|
54
|
+
|
|
55
|
+
const value = storage.getItem(key);
|
|
56
|
+
|
|
57
|
+
assert(value !== null, "777098");
|
|
58
|
+
|
|
59
|
+
storage.setItem(key, value);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
get length() {
|
|
63
|
+
return entries.length;
|
|
64
|
+
},
|
|
65
|
+
key: index => {
|
|
66
|
+
const entry = entries[index];
|
|
67
|
+
|
|
68
|
+
if (entry === undefined) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return entry.key;
|
|
73
|
+
},
|
|
74
|
+
removeItem: key => {
|
|
75
|
+
const entry = entries.find(entry => entry.key === key);
|
|
76
|
+
|
|
77
|
+
if (entry === undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
sessionStorage.removeItem(getSessionStorageKey(entry.key));
|
|
82
|
+
|
|
83
|
+
const index = entries.indexOf(entry);
|
|
84
|
+
|
|
85
|
+
entries.splice(index, 1);
|
|
86
|
+
},
|
|
87
|
+
clear: () => {
|
|
88
|
+
for (let i = 0; i < storage.length; i++) {
|
|
89
|
+
const key = storage.key(i);
|
|
90
|
+
assert(key !== null, "290875");
|
|
91
|
+
storage.removeItem(key);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
getItem: key => {
|
|
95
|
+
const entry = entries.find(entry => entry.key === key);
|
|
96
|
+
if (entry === undefined) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return entry.value;
|
|
100
|
+
},
|
|
101
|
+
setItem: (key, value) => {
|
|
102
|
+
if (isPersistenceEnabled) {
|
|
103
|
+
sessionStorage.setItem(getSessionStorageKey(key), value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
update: {
|
|
107
|
+
const entry = entries.find(entry => entry.key === key);
|
|
108
|
+
|
|
109
|
+
if (entry === undefined) {
|
|
110
|
+
break update;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
entry.value = value;
|
|
114
|
+
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
entries.push({ key, value });
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return storage;
|
|
123
|
+
}
|
|
@@ -34,7 +34,8 @@ export function manageOptimizedDeps(params: {
|
|
|
34
34
|
const moduleNames_include = [
|
|
35
35
|
"oidc-spa/react-spa",
|
|
36
36
|
"oidc-spa/entrypoint",
|
|
37
|
-
"oidc-spa/keycloak"
|
|
37
|
+
"oidc-spa/keycloak",
|
|
38
|
+
"oidc-spa/core"
|
|
38
39
|
];
|
|
39
40
|
|
|
40
41
|
for (const moduleName of moduleNames_include) {
|
|
@@ -55,6 +56,8 @@ export function manageOptimizedDeps(params: {
|
|
|
55
56
|
moduleNames_include.push("zod");
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
moduleNames_include.push("oidc-spa");
|
|
60
|
+
|
|
58
61
|
((userConfig.optimizeDeps ??= {}).include ??= []).push(...moduleNames_include);
|
|
59
62
|
}
|
|
60
63
|
break;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type LazySessionStorage = {
|
|
2
2
|
readonly length: number;
|
|
3
3
|
clear(): void;
|
|
4
4
|
getItem(key: string): string | null;
|
|
@@ -7,6 +7,6 @@ export type EphemeralSessionStorage = {
|
|
|
7
7
|
setItem(key: string, value: string): void;
|
|
8
8
|
persistCurrentStateAndSubsequentChanges: () => void;
|
|
9
9
|
};
|
|
10
|
-
export declare function
|
|
11
|
-
|
|
12
|
-
}):
|
|
10
|
+
export declare function createLazySessionStorage(params: {
|
|
11
|
+
storageId: string;
|
|
12
|
+
}): LazySessionStorage;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLazySessionStorage = createLazySessionStorage;
|
|
4
|
+
const assert_1 = require("../tools/tsafe/assert");
|
|
5
|
+
function createLazySessionStorage(params) {
|
|
6
|
+
const { storageId } = params;
|
|
7
|
+
const sessionStoragePrefix = `lazy-session-storage:${storageId}:`;
|
|
8
|
+
const getSessionStorageKey = (key) => `${sessionStoragePrefix}${key}`;
|
|
9
|
+
const entries = [];
|
|
10
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
11
|
+
const key = sessionStorage.key(i);
|
|
12
|
+
(0, assert_1.assert)(key !== null, "470498");
|
|
13
|
+
if (!key.startsWith(sessionStoragePrefix)) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const value = sessionStorage.getItem(key);
|
|
17
|
+
(0, assert_1.assert)(value !== null, "846771");
|
|
18
|
+
sessionStorage.removeItem(key);
|
|
19
|
+
entries.push({
|
|
20
|
+
key: key.slice(sessionStoragePrefix.length),
|
|
21
|
+
value
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
let isPersistenceEnabled = false;
|
|
25
|
+
const storage = {
|
|
26
|
+
persistCurrentStateAndSubsequentChanges: () => {
|
|
27
|
+
isPersistenceEnabled = true;
|
|
28
|
+
for (let i = 0; i < storage.length; i++) {
|
|
29
|
+
const key = storage.key(i);
|
|
30
|
+
(0, assert_1.assert)(key !== null, "803385");
|
|
31
|
+
const value = storage.getItem(key);
|
|
32
|
+
(0, assert_1.assert)(value !== null, "777098");
|
|
33
|
+
storage.setItem(key, value);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
get length() {
|
|
37
|
+
return entries.length;
|
|
38
|
+
},
|
|
39
|
+
key: index => {
|
|
40
|
+
const entry = entries[index];
|
|
41
|
+
if (entry === undefined) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return entry.key;
|
|
45
|
+
},
|
|
46
|
+
removeItem: key => {
|
|
47
|
+
const entry = entries.find(entry => entry.key === key);
|
|
48
|
+
if (entry === undefined) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
sessionStorage.removeItem(getSessionStorageKey(entry.key));
|
|
52
|
+
const index = entries.indexOf(entry);
|
|
53
|
+
entries.splice(index, 1);
|
|
54
|
+
},
|
|
55
|
+
clear: () => {
|
|
56
|
+
for (let i = 0; i < storage.length; i++) {
|
|
57
|
+
const key = storage.key(i);
|
|
58
|
+
(0, assert_1.assert)(key !== null, "290875");
|
|
59
|
+
storage.removeItem(key);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
getItem: key => {
|
|
63
|
+
const entry = entries.find(entry => entry.key === key);
|
|
64
|
+
if (entry === undefined) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return entry.value;
|
|
68
|
+
},
|
|
69
|
+
setItem: (key, value) => {
|
|
70
|
+
if (isPersistenceEnabled) {
|
|
71
|
+
sessionStorage.setItem(getSessionStorageKey(key), value);
|
|
72
|
+
}
|
|
73
|
+
update: {
|
|
74
|
+
const entry = entries.find(entry => entry.key === key);
|
|
75
|
+
if (entry === undefined) {
|
|
76
|
+
break update;
|
|
77
|
+
}
|
|
78
|
+
entry.value = value;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
entries.push({ key, value });
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return storage;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=lazySessionStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazySessionStorage.js","sourceRoot":"","sources":["../src/tools/lazySessionStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -59,7 +59,8 @@ function manageOptimizedDeps(params) {
|
|
|
59
59
|
const moduleNames_include = [
|
|
60
60
|
"oidc-spa/react-spa",
|
|
61
61
|
"oidc-spa/entrypoint",
|
|
62
|
-
"oidc-spa/keycloak"
|
|
62
|
+
"oidc-spa/keycloak",
|
|
63
|
+
"oidc-spa/core"
|
|
63
64
|
];
|
|
64
65
|
for (const moduleName of moduleNames_include) {
|
|
65
66
|
(0, assert_1.assert)(moduleNames.includes(moduleName));
|
|
@@ -76,6 +77,7 @@ function manageOptimizedDeps(params) {
|
|
|
76
77
|
if (isZodInstalled) {
|
|
77
78
|
moduleNames_include.push("zod");
|
|
78
79
|
}
|
|
80
|
+
moduleNames_include.push("oidc-spa");
|
|
79
81
|
((_b = (userConfig.optimizeDeps ?? (userConfig.optimizeDeps = {}))).include ?? (_b.include = [])).push(...moduleNames_include);
|
|
80
82
|
}
|
|
81
83
|
break;
|