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
package/src/core/createOidc.ts
CHANGED
|
@@ -25,12 +25,7 @@ import { notifyOtherTabsOfLogin, getPrOtherTabLogin } from "./loginPropagationTo
|
|
|
25
25
|
import { getConfigId } from "./configId";
|
|
26
26
|
import { oidcClientTsUserToTokens } from "./oidcClientTsUserToTokens";
|
|
27
27
|
import { loginSilent } from "./loginSilent";
|
|
28
|
-
import {
|
|
29
|
-
authResponseToUrl,
|
|
30
|
-
getPersistedRedirectAuthResponses,
|
|
31
|
-
setPersistedRedirectAuthResponses,
|
|
32
|
-
type AuthResponse
|
|
33
|
-
} from "./AuthResponse";
|
|
28
|
+
import { authResponseToUrl, type AuthResponse } from "./AuthResponse";
|
|
34
29
|
import { getRootRelativeOriginalLocationHref, getRedirectAuthResponse } from "./earlyInit";
|
|
35
30
|
import { getPersistedAuthState, persistAuthState } from "./persistedAuthState";
|
|
36
31
|
import type { Oidc } from "./Oidc";
|
|
@@ -40,7 +35,7 @@ import {
|
|
|
40
35
|
createLoginOrGoToAuthServer,
|
|
41
36
|
getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError
|
|
42
37
|
} from "./loginOrGoToAuthServer";
|
|
43
|
-
import {
|
|
38
|
+
import { createLazySessionStorage } from "../tools/lazySessionStorage";
|
|
44
39
|
import {
|
|
45
40
|
startLoginOrRefreshProcess,
|
|
46
41
|
waitForAllOtherOngoingLoginOrRefreshProcessesToComplete
|
|
@@ -53,6 +48,11 @@ import { prShouldLoadApp } from "./prShouldLoadApp";
|
|
|
53
48
|
import { getBASE_URL } from "./BASE_URL";
|
|
54
49
|
import { getIsLikelyDevServer } from "../tools/isLikelyDevServer";
|
|
55
50
|
import { createObjectThatThrowsIfAccessed } from "../tools/createObjectThatThrowsIfAccessed";
|
|
51
|
+
import {
|
|
52
|
+
evtIsThereMoreThanOneInstanceThatCantUserIframes,
|
|
53
|
+
notifyNewInstanceThatCantUseIframes
|
|
54
|
+
} from "./instancesThatCantUseIframes";
|
|
55
|
+
import { getDesiredPostLoginRedirectUrl } from "./desiredPostLoginRedirectUrl";
|
|
56
56
|
|
|
57
57
|
// NOTE: Replaced at build time
|
|
58
58
|
const VERSION = "{{OIDC_SPA_VERSION}}";
|
|
@@ -107,19 +107,6 @@ export type ParamsOfCreateOidc<
|
|
|
107
107
|
* extraTokenParams: { selectedCustomer: "xxx" }
|
|
108
108
|
*/
|
|
109
109
|
extraTokenParams?: Record<string, string | undefined> | (() => Record<string, string | undefined>);
|
|
110
|
-
/**
|
|
111
|
-
* Usage discouraged, it's here because we don't want to assume too much on your
|
|
112
|
-
* usecase but I can't think of a scenario where you would want anything
|
|
113
|
-
* other than the current page.
|
|
114
|
-
*
|
|
115
|
-
* Where to redirect after successful login.
|
|
116
|
-
* Default: window.location.href (here)
|
|
117
|
-
*
|
|
118
|
-
* It does not need to include the origin, eg: "/dashboard"
|
|
119
|
-
*
|
|
120
|
-
* This parameter can also be passed to login() directly as `redirectUrl`.
|
|
121
|
-
*/
|
|
122
|
-
postLoginRedirectUrl?: string;
|
|
123
110
|
|
|
124
111
|
decodedIdTokenSchema?: {
|
|
125
112
|
parse: (decodedIdToken_original: Oidc.Tokens.DecodedIdToken_OidcCoreSpec) => DecodedIdToken;
|
|
@@ -149,9 +136,43 @@ export type ParamsOfCreateOidc<
|
|
|
149
136
|
autoLogin?: AutoLogin;
|
|
150
137
|
|
|
151
138
|
/**
|
|
139
|
+
* Determines how session restoration is handled.
|
|
140
|
+
* Session restoration allows users to stay logged in between visits
|
|
141
|
+
* without needing to explicitly sign in each time.
|
|
142
|
+
*
|
|
143
|
+
* Options:
|
|
144
|
+
*
|
|
145
|
+
* - **"auto" (default)**:
|
|
146
|
+
* Automatically selects the best method.
|
|
147
|
+
* If the app’s domain shares a common parent domain with the authorization endpoint,
|
|
148
|
+
* an iframe is used for silent session restoration.
|
|
149
|
+
* Otherwise, a full-page redirect is used.
|
|
150
|
+
*
|
|
151
|
+
* - **"full page redirect"**:
|
|
152
|
+
* Forces full-page reloads for session restoration.
|
|
153
|
+
* Use this if your application is served with a restrictive CSP
|
|
154
|
+
* (e.g., `Content-Security-Policy: frame-ancestors "none"`)
|
|
155
|
+
* or `X-Frame-Options: DENY`, and you cannot modify those headers.
|
|
156
|
+
* This mode provides a slightly less seamless UX and will lead oidc-spa to
|
|
157
|
+
* store tokens in `localStorage` if multiple OIDC clients are used
|
|
158
|
+
* (e.g., your app communicates with several APIs).
|
|
159
|
+
*
|
|
160
|
+
* - **"iframe"**:
|
|
161
|
+
* Forces iframe-based session restoration.
|
|
162
|
+
* In development, if you go in your browser setting and allow your auth server’s domain
|
|
163
|
+
* to set third-party cookies this value will let you test your app
|
|
164
|
+
* with the local dev server as it will behave in production.
|
|
165
|
+
*
|
|
166
|
+
* See: https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration
|
|
167
|
+
*/
|
|
168
|
+
sessionRestorationMethod?: "iframe" | "full page redirect" | "auto";
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @deprecated Use `sessionRestorationMethod: "full page redirect"` instead.
|
|
172
|
+
*
|
|
152
173
|
* Default: false
|
|
153
174
|
*
|
|
154
|
-
* See: https://docs.oidc-spa.dev/v/v8/resources/
|
|
175
|
+
* See: https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration
|
|
155
176
|
*/
|
|
156
177
|
noIframe?: boolean;
|
|
157
178
|
|
|
@@ -204,28 +225,28 @@ export type ParamsOfCreateOidc<
|
|
|
204
225
|
|
|
205
226
|
/** @deprecated: Use BASE_URL (same thing, just renamed). */
|
|
206
227
|
homeUrl?: string;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* This parameter is irrelevant in most usecases.
|
|
231
|
+
* It tells where to redirect after a successful login or autoLogin.
|
|
232
|
+
*
|
|
233
|
+
* If you are not in autoLogin mode there is absolutely no reason to use
|
|
234
|
+
* this parameter since you can pass `login({ redirectUrl: "..." })`.
|
|
235
|
+
*
|
|
236
|
+
* It can only be useful in some edge case with `autoLogin: true`
|
|
237
|
+
* When you want to precisely redirect somewhere after login.
|
|
238
|
+
*
|
|
239
|
+
* This can make sense if you have multiple clients to talk with different
|
|
240
|
+
* API and no iframe capabilities.
|
|
241
|
+
*/
|
|
242
|
+
postLoginRedirectUrl?: string;
|
|
207
243
|
};
|
|
208
244
|
|
|
209
245
|
const globalContext = {
|
|
210
246
|
prOidcByConfigId: new Map<string, Promise<Oidc<any>>>(),
|
|
211
|
-
hasLogoutBeenCalled: id<boolean>(false)
|
|
212
|
-
evtRequestToPersistTokens: createEvt<{ configIdOfInstancePostingTheRequest: string }>()
|
|
247
|
+
hasLogoutBeenCalled: id<boolean>(false)
|
|
213
248
|
};
|
|
214
249
|
|
|
215
|
-
globalContext.evtRequestToPersistTokens.subscribe(() => {
|
|
216
|
-
const { authResponse } = getRedirectAuthResponse();
|
|
217
|
-
|
|
218
|
-
if (authResponse === undefined) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const { authResponses } = getPersistedRedirectAuthResponses();
|
|
223
|
-
|
|
224
|
-
setPersistedRedirectAuthResponses({
|
|
225
|
-
authResponses: [...authResponses, authResponse]
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
250
|
/** @see: https://docs.oidc-spa.dev/v/v8/usage */
|
|
230
251
|
export async function createOidc<
|
|
231
252
|
DecodedIdToken extends Record<string, unknown> = Oidc.Tokens.DecodedIdToken_OidcCoreSpec,
|
|
@@ -242,7 +263,7 @@ export async function createOidc<
|
|
|
242
263
|
}
|
|
243
264
|
}
|
|
244
265
|
|
|
245
|
-
const { issuerUri: issuerUri_params, clientId,
|
|
266
|
+
const { issuerUri: issuerUri_params, clientId, debugLogs, ...rest } = params;
|
|
246
267
|
|
|
247
268
|
const issuerUri = toFullyQualifiedUrl({
|
|
248
269
|
urlish: issuerUri_params,
|
|
@@ -298,7 +319,6 @@ export async function createOidc<
|
|
|
298
319
|
const oidc = await createOidc_nonMemoized(rest, {
|
|
299
320
|
issuerUri,
|
|
300
321
|
clientId,
|
|
301
|
-
scopes,
|
|
302
322
|
configId,
|
|
303
323
|
log
|
|
304
324
|
});
|
|
@@ -312,14 +332,10 @@ export async function createOidc_nonMemoized<
|
|
|
312
332
|
DecodedIdToken extends Record<string, unknown>,
|
|
313
333
|
AutoLogin extends boolean
|
|
314
334
|
>(
|
|
315
|
-
params: Omit<
|
|
316
|
-
ParamsOfCreateOidc<DecodedIdToken, AutoLogin>,
|
|
317
|
-
"issuerUri" | "clientId" | "scopes" | "debugLogs"
|
|
318
|
-
>,
|
|
335
|
+
params: Omit<ParamsOfCreateOidc<DecodedIdToken, AutoLogin>, "issuerUri" | "clientId" | "debugLogs">,
|
|
319
336
|
preProcessedParams: {
|
|
320
337
|
issuerUri: string;
|
|
321
338
|
clientId: string;
|
|
322
|
-
scopes: string[];
|
|
323
339
|
configId: string;
|
|
324
340
|
log: typeof console.log | undefined;
|
|
325
341
|
}
|
|
@@ -357,12 +373,13 @@ export async function createOidc_nonMemoized<
|
|
|
357
373
|
__unsafe_clientSecret,
|
|
358
374
|
__unsafe_useIdTokenAsAccessToken = false,
|
|
359
375
|
__metadata,
|
|
360
|
-
|
|
376
|
+
scopes = ["openid", "profile"],
|
|
377
|
+
sessionRestorationMethod = params.autoLogin === true ? "full page redirect" : "auto"
|
|
361
378
|
} = params;
|
|
362
379
|
|
|
363
380
|
const BASE_URL_params = params.BASE_URL ?? params.homeUrl;
|
|
364
381
|
|
|
365
|
-
const { issuerUri, clientId,
|
|
382
|
+
const { issuerUri, clientId, configId, log } = preProcessedParams;
|
|
366
383
|
|
|
367
384
|
const getExtraQueryParams = (() => {
|
|
368
385
|
if (extraQueryParamsOrGetter === undefined) {
|
|
@@ -422,8 +439,7 @@ export async function createOidc_nonMemoized<
|
|
|
422
439
|
issuerUri,
|
|
423
440
|
clientId,
|
|
424
441
|
scopes,
|
|
425
|
-
|
|
426
|
-
homeUrlAndRedirectUri
|
|
442
|
+
oidcRedirectUri: homeUrlAndRedirectUri
|
|
427
443
|
},
|
|
428
444
|
null,
|
|
429
445
|
2
|
|
@@ -435,8 +451,15 @@ export async function createOidc_nonMemoized<
|
|
|
435
451
|
const oidcMetadata = __metadata ?? (await fetchOidcMetadata({ issuerUri }));
|
|
436
452
|
|
|
437
453
|
const canUseIframe = (() => {
|
|
438
|
-
|
|
439
|
-
|
|
454
|
+
switch (sessionRestorationMethod) {
|
|
455
|
+
case "auto":
|
|
456
|
+
break;
|
|
457
|
+
case "full page redirect":
|
|
458
|
+
return false;
|
|
459
|
+
case "iframe":
|
|
460
|
+
return true;
|
|
461
|
+
default:
|
|
462
|
+
assert<Equals<typeof sessionRestorationMethod, never>>;
|
|
440
463
|
}
|
|
441
464
|
|
|
442
465
|
third_party_cookies: {
|
|
@@ -515,7 +538,7 @@ export async function createOidc_nonMemoized<
|
|
|
515
538
|
log?.(
|
|
516
539
|
[
|
|
517
540
|
"Detected localhost environment.",
|
|
518
|
-
"\nWhen reloading while logged in, you
|
|
541
|
+
"\nWhen reloading while logged in, you will briefly see",
|
|
519
542
|
"some URL params appear in the address bar.",
|
|
520
543
|
"\nThis happens because session restore via iframe is disabled,",
|
|
521
544
|
"the browser treats your auth server as a third party.",
|
|
@@ -544,7 +567,7 @@ export async function createOidc_nonMemoized<
|
|
|
544
567
|
];
|
|
545
568
|
})(),
|
|
546
569
|
"\n\nMore info:",
|
|
547
|
-
"https://docs.oidc-spa.dev/v/v8/resources/
|
|
570
|
+
"https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration"
|
|
548
571
|
].join(" ")
|
|
549
572
|
);
|
|
550
573
|
} else {
|
|
@@ -574,7 +597,7 @@ export async function createOidc_nonMemoized<
|
|
|
574
597
|
];
|
|
575
598
|
})(),
|
|
576
599
|
"\nMore info:",
|
|
577
|
-
"https://docs.oidc-spa.dev/v/v8/resources/
|
|
600
|
+
"https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration"
|
|
578
601
|
].join(" ")
|
|
579
602
|
);
|
|
580
603
|
}
|
|
@@ -585,7 +608,16 @@ export async function createOidc_nonMemoized<
|
|
|
585
608
|
return true;
|
|
586
609
|
})();
|
|
587
610
|
|
|
588
|
-
|
|
611
|
+
notifyNewInstanceThatCantUseIframes();
|
|
612
|
+
|
|
613
|
+
if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
|
|
614
|
+
log?.(
|
|
615
|
+
[
|
|
616
|
+
"More than one oidc instance can't use iframe",
|
|
617
|
+
"falling back to persisting tokens in session storage"
|
|
618
|
+
].join(" ")
|
|
619
|
+
);
|
|
620
|
+
}
|
|
589
621
|
|
|
590
622
|
const oidcClientTsUserManager =
|
|
591
623
|
oidcMetadata === undefined
|
|
@@ -606,27 +638,18 @@ export async function createOidc_nonMemoized<
|
|
|
606
638
|
userStore: new WebStorageStateStore({
|
|
607
639
|
store: (() => {
|
|
608
640
|
if (canUseIframe) {
|
|
609
|
-
isUserStoreInMemoryOnly = true;
|
|
610
641
|
return new InMemoryWebStorage();
|
|
611
642
|
}
|
|
612
643
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const storage = createEphemeralSessionStorage({
|
|
616
|
-
sessionStorageTtlMs: 3 * 60_000
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
const { evtRequestToPersistTokens } = globalContext;
|
|
620
|
-
|
|
621
|
-
evtRequestToPersistTokens.subscribe(
|
|
622
|
-
({ configIdOfInstancePostingTheRequest }) => {
|
|
623
|
-
if (configIdOfInstancePostingTheRequest === configId) {
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
644
|
+
const storage = createLazySessionStorage({ storageId: configId });
|
|
626
645
|
|
|
646
|
+
if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
|
|
647
|
+
storage.persistCurrentStateAndSubsequentChanges();
|
|
648
|
+
} else {
|
|
649
|
+
evtIsThereMoreThanOneInstanceThatCantUserIframes.subscribe(() => {
|
|
627
650
|
storage.persistCurrentStateAndSubsequentChanges();
|
|
628
|
-
}
|
|
629
|
-
|
|
651
|
+
});
|
|
652
|
+
}
|
|
630
653
|
|
|
631
654
|
return storage;
|
|
632
655
|
})()
|
|
@@ -675,75 +698,67 @@ export async function createOidc_nonMemoized<
|
|
|
675
698
|
});
|
|
676
699
|
}
|
|
677
700
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
701
|
+
restore_from_session_storage: {
|
|
702
|
+
if (canUseIframe) {
|
|
703
|
+
break restore_from_session_storage;
|
|
704
|
+
}
|
|
682
705
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
706
|
+
if (!evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
|
|
707
|
+
break restore_from_session_storage;
|
|
708
|
+
}
|
|
686
709
|
|
|
687
|
-
|
|
688
|
-
break from_memory;
|
|
689
|
-
}
|
|
710
|
+
let oidcClientTsUser: OidcClientTsUser | null;
|
|
690
711
|
|
|
691
|
-
|
|
712
|
+
try {
|
|
713
|
+
oidcClientTsUser = await oidcClientTsUserManager.getUser();
|
|
714
|
+
} catch {
|
|
715
|
+
// NOTE: Not sure if it can throw, but let's be safe.
|
|
716
|
+
oidcClientTsUser = null;
|
|
717
|
+
try {
|
|
718
|
+
await oidcClientTsUserManager.removeUser();
|
|
719
|
+
} catch {}
|
|
720
|
+
}
|
|
692
721
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
}
|
|
722
|
+
if (oidcClientTsUser === null) {
|
|
723
|
+
break restore_from_session_storage;
|
|
724
|
+
}
|
|
697
725
|
|
|
698
|
-
|
|
699
|
-
break from_memory;
|
|
700
|
-
}
|
|
726
|
+
log?.("Session was restored from session storage");
|
|
701
727
|
|
|
702
|
-
|
|
728
|
+
return {
|
|
729
|
+
oidcClientTsUser,
|
|
730
|
+
backFromAuthServer: undefined
|
|
731
|
+
};
|
|
732
|
+
}
|
|
703
733
|
|
|
704
|
-
|
|
734
|
+
handle_redirect_auth_response: {
|
|
735
|
+
let stateDataAndAuthResponse:
|
|
736
|
+
| { stateData: StateData.Redirect; authResponse: AuthResponse }
|
|
737
|
+
| undefined = undefined;
|
|
705
738
|
|
|
706
|
-
|
|
739
|
+
{
|
|
740
|
+
const { authResponse, clearAuthResponse } = getRedirectAuthResponse();
|
|
707
741
|
|
|
708
|
-
|
|
742
|
+
if (authResponse === undefined) {
|
|
743
|
+
break handle_redirect_auth_response;
|
|
709
744
|
}
|
|
710
745
|
|
|
711
|
-
|
|
712
|
-
// setup where one instance would need to redirect before
|
|
713
|
-
// the authResponse in memory had the chance to be processed.
|
|
714
|
-
// This can only happen if:
|
|
715
|
-
// 1) There are multiple oidc instances in the App.
|
|
716
|
-
// 2) They are instantiated in a non deterministic order.
|
|
717
|
-
// 3) We can't use iframe
|
|
718
|
-
// We practically never persist the auth response and do it only in session
|
|
719
|
-
// an ephemeral session storage, when we know it's gonna be required.
|
|
720
|
-
{
|
|
721
|
-
const { authResponses } = getPersistedRedirectAuthResponses();
|
|
722
|
-
|
|
723
|
-
for (const authResponse of authResponses) {
|
|
724
|
-
const stateData = getStateData({ stateUrlParamValue: authResponse.state });
|
|
725
|
-
|
|
726
|
-
if (stateData === undefined) {
|
|
727
|
-
continue;
|
|
728
|
-
}
|
|
746
|
+
const stateData = getStateData({ stateUrlParamValue: authResponse.state });
|
|
729
747
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
748
|
+
if (stateData === undefined) {
|
|
749
|
+
clearAuthResponse();
|
|
750
|
+
break handle_redirect_auth_response;
|
|
751
|
+
}
|
|
733
752
|
|
|
734
|
-
|
|
753
|
+
if (stateData.configId !== configId) {
|
|
754
|
+
break handle_redirect_auth_response;
|
|
755
|
+
}
|
|
735
756
|
|
|
736
|
-
|
|
737
|
-
authResponses: authResponses.filter(
|
|
738
|
-
authResponse_i => authResponse_i !== authResponse
|
|
739
|
-
)
|
|
740
|
-
});
|
|
757
|
+
assert(stateData.context === "redirect", "3229492");
|
|
741
758
|
|
|
742
|
-
|
|
759
|
+
clearAuthResponse();
|
|
743
760
|
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
}
|
|
761
|
+
stateDataAndAuthResponse = { stateData, authResponse };
|
|
747
762
|
}
|
|
748
763
|
|
|
749
764
|
if (stateDataAndAuthResponse === undefined) {
|
|
@@ -856,39 +871,6 @@ export async function createOidc_nonMemoized<
|
|
|
856
871
|
}
|
|
857
872
|
}
|
|
858
873
|
|
|
859
|
-
// NOTE: We almost never persist tokens, we have to only to support edge case
|
|
860
|
-
// of multiple oidc instance in a single App with no iframe support.
|
|
861
|
-
restore_from_session_storage: {
|
|
862
|
-
assert(isUserStoreInMemoryOnly !== undefined, "3392204");
|
|
863
|
-
|
|
864
|
-
if (isUserStoreInMemoryOnly) {
|
|
865
|
-
break restore_from_session_storage;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
let oidcClientTsUser: OidcClientTsUser | null;
|
|
869
|
-
|
|
870
|
-
try {
|
|
871
|
-
oidcClientTsUser = await oidcClientTsUserManager.getUser();
|
|
872
|
-
} catch {
|
|
873
|
-
// NOTE: Not sure if it can throw, but let's be safe.
|
|
874
|
-
oidcClientTsUser = null;
|
|
875
|
-
try {
|
|
876
|
-
await oidcClientTsUserManager.removeUser();
|
|
877
|
-
} catch {}
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (oidcClientTsUser === null) {
|
|
881
|
-
break restore_from_session_storage;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
log?.("Restored the auth from ephemeral session storage");
|
|
885
|
-
|
|
886
|
-
return {
|
|
887
|
-
oidcClientTsUser,
|
|
888
|
-
backFromAuthServer: undefined
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
|
|
892
874
|
silent_login_if_possible_and_auto_login: {
|
|
893
875
|
const persistedAuthState = getPersistedAuthState({ configId });
|
|
894
876
|
|
|
@@ -956,7 +938,7 @@ export async function createOidc_nonMemoized<
|
|
|
956
938
|
redirectUri: homeUrlAndRedirectUri,
|
|
957
939
|
clientId,
|
|
958
940
|
issuerUri,
|
|
959
|
-
|
|
941
|
+
canUseIframe
|
|
960
942
|
});
|
|
961
943
|
}
|
|
962
944
|
|
|
@@ -1002,8 +984,6 @@ export async function createOidc_nonMemoized<
|
|
|
1002
984
|
) {
|
|
1003
985
|
log?.("Performing auto login with redirect");
|
|
1004
986
|
|
|
1005
|
-
persistAuthState({ configId, state: undefined });
|
|
1006
|
-
|
|
1007
987
|
completeLoginOrRefreshProcess();
|
|
1008
988
|
|
|
1009
989
|
if (autoLogin && persistedAuthState !== "logged in") {
|
|
@@ -1015,16 +995,20 @@ export async function createOidc_nonMemoized<
|
|
|
1015
995
|
getPrSafelyRestoredFromBfCacheAfterLoginBackNavigationOrInitializationError()
|
|
1016
996
|
});
|
|
1017
997
|
|
|
1018
|
-
if (persistedAuthState === "logged in") {
|
|
1019
|
-
globalContext.evtRequestToPersistTokens.post({
|
|
1020
|
-
configIdOfInstancePostingTheRequest: configId
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
998
|
await loginOrGoToAuthServer({
|
|
1025
999
|
action: "login",
|
|
1026
1000
|
doForceReloadOnBfCache: true,
|
|
1027
|
-
redirectUrl:
|
|
1001
|
+
redirectUrl: (() => {
|
|
1002
|
+
if (postLoginRedirectUrl_default) {
|
|
1003
|
+
return postLoginRedirectUrl_default;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (!evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
|
|
1007
|
+
return getRootRelativeOriginalLocationHref();
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
return getDesiredPostLoginRedirectUrl() ?? window.location.href;
|
|
1011
|
+
})(),
|
|
1028
1012
|
// NOTE: Wether or not it's the preferred behavior, pushing to history
|
|
1029
1013
|
// only works on user interaction so it have to be false
|
|
1030
1014
|
doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
|
|
@@ -1040,7 +1024,10 @@ export async function createOidc_nonMemoized<
|
|
|
1040
1024
|
}
|
|
1041
1025
|
|
|
1042
1026
|
return "ensure no interaction";
|
|
1043
|
-
})()
|
|
1027
|
+
})(),
|
|
1028
|
+
preRedirectHook: () => {
|
|
1029
|
+
persistAuthState({ configId, state: undefined });
|
|
1030
|
+
}
|
|
1044
1031
|
});
|
|
1045
1032
|
}
|
|
1046
1033
|
|
|
@@ -1161,7 +1148,8 @@ export async function createOidc_nonMemoized<
|
|
|
1161
1148
|
interaction:
|
|
1162
1149
|
getPersistedAuthState({ configId }) === "explicitly logged out"
|
|
1163
1150
|
? "ensure interaction"
|
|
1164
|
-
: "directly redirect if active session show login otherwise"
|
|
1151
|
+
: "directly redirect if active session show login otherwise",
|
|
1152
|
+
preRedirectHook: undefined
|
|
1165
1153
|
});
|
|
1166
1154
|
},
|
|
1167
1155
|
initializationError: undefined
|
|
@@ -1233,6 +1221,7 @@ export async function createOidc_nonMemoized<
|
|
|
1233
1221
|
state: {
|
|
1234
1222
|
stateDescription: "logged in",
|
|
1235
1223
|
refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
|
|
1224
|
+
serverDateNow: currentTokens.getServerDateNow(),
|
|
1236
1225
|
idleSessionLifetimeInSeconds
|
|
1237
1226
|
}
|
|
1238
1227
|
});
|
|
@@ -1382,10 +1371,6 @@ export async function createOidc_nonMemoized<
|
|
|
1382
1371
|
prUnlock: new Promise<never>(() => {})
|
|
1383
1372
|
});
|
|
1384
1373
|
|
|
1385
|
-
globalContext.evtRequestToPersistTokens.post({
|
|
1386
|
-
configIdOfInstancePostingTheRequest: configId
|
|
1387
|
-
});
|
|
1388
|
-
|
|
1389
1374
|
await loginOrGoToAuthServer({
|
|
1390
1375
|
action: "login",
|
|
1391
1376
|
redirectUrl: window.location.href,
|
|
@@ -1393,7 +1378,8 @@ export async function createOidc_nonMemoized<
|
|
|
1393
1378
|
extraQueryParams_local: undefined,
|
|
1394
1379
|
transformUrlBeforeRedirect_local: undefined,
|
|
1395
1380
|
doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
|
|
1396
|
-
interaction: "directly redirect if active session show login otherwise"
|
|
1381
|
+
interaction: "directly redirect if active session show login otherwise",
|
|
1382
|
+
preRedirectHook: undefined
|
|
1397
1383
|
});
|
|
1398
1384
|
assert(false, "136134");
|
|
1399
1385
|
};
|
|
@@ -1514,6 +1500,7 @@ export async function createOidc_nonMemoized<
|
|
|
1514
1500
|
state: {
|
|
1515
1501
|
stateDescription: "logged in",
|
|
1516
1502
|
refreshTokenExpirationTime: currentTokens.refreshTokenExpirationTime,
|
|
1503
|
+
serverDateNow: currentTokens.getServerDateNow(),
|
|
1517
1504
|
idleSessionLifetimeInSeconds
|
|
1518
1505
|
}
|
|
1519
1506
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
let postLoginRedirectUrl: string | undefined;
|
|
2
|
+
|
|
3
|
+
export function setDesiredPostLoginRedirectUrl(params: { postLoginRedirectUrl: string | undefined }) {
|
|
4
|
+
postLoginRedirectUrl = params.postLoginRedirectUrl;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getDesiredPostLoginRedirectUrl() {
|
|
8
|
+
return postLoginRedirectUrl;
|
|
9
|
+
}
|
package/src/core/diagnostic.ts
CHANGED
|
@@ -90,9 +90,9 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
90
90
|
redirectUri: string;
|
|
91
91
|
issuerUri: string;
|
|
92
92
|
clientId: string;
|
|
93
|
-
|
|
93
|
+
canUseIframe: boolean;
|
|
94
94
|
}): Promise<OidcInitializationError> {
|
|
95
|
-
const { redirectUri, issuerUri, clientId,
|
|
95
|
+
const { redirectUri, issuerUri, clientId, canUseIframe } = params;
|
|
96
96
|
|
|
97
97
|
check_if_well_known_endpoint_is_reachable: {
|
|
98
98
|
const isValid = await getIsValidRemoteJson(`${issuerUri}${WELL_KNOWN_PATH}`);
|
|
@@ -105,7 +105,7 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
iframe_blocked: {
|
|
108
|
-
if (
|
|
108
|
+
if (!canUseIframe) {
|
|
109
109
|
break iframe_blocked;
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -189,7 +189,7 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
189
189
|
messageOrCause: [
|
|
190
190
|
`${redirectUri} is currently served by your web server with the HTTP header \`${key_problem}: ${headers[key_problem]}\`.\n`,
|
|
191
191
|
"This header prevents the silent sign-in process from working.\n",
|
|
192
|
-
"Refer to this documentation page to fix this issue: https://docs.oidc-spa.dev/v/v8/resources/
|
|
192
|
+
"Refer to this documentation page to fix this issue: https://docs.oidc-spa.dev/v/v8/resources/third-party-cookies-and-session-restoration"
|
|
193
193
|
].join(" ")
|
|
194
194
|
});
|
|
195
195
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createStatefulEvt } from "../tools/StatefulEvt";
|
|
2
|
+
|
|
3
|
+
const SESSION_STORAGE_KEY = "oidc-spa:more-than-one-instance-cant-use-iframe";
|
|
4
|
+
|
|
5
|
+
export const evtIsThereMoreThanOneInstanceThatCantUserIframes = createStatefulEvt(
|
|
6
|
+
() => sessionStorage.getItem(SESSION_STORAGE_KEY) !== null
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
let count = 0;
|
|
10
|
+
|
|
11
|
+
export function notifyNewInstanceThatCantUseIframes() {
|
|
12
|
+
count++;
|
|
13
|
+
|
|
14
|
+
if (count === 1) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (evtIsThereMoreThanOneInstanceThatCantUserIframes.current) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, "true");
|
|
23
|
+
evtIsThereMoreThanOneInstanceThatCantUserIframes.current = true;
|
|
24
|
+
}
|
|
@@ -30,6 +30,7 @@ namespace Params {
|
|
|
30
30
|
| "ensure no interaction"
|
|
31
31
|
| "ensure interaction"
|
|
32
32
|
| "directly redirect if active session show login otherwise";
|
|
33
|
+
preRedirectHook: (() => void) | undefined;
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
export type GoToAuthServer = Common & {
|
|
@@ -305,6 +306,10 @@ export function createLoginOrGoToAuthServer(params: {
|
|
|
305
306
|
|
|
306
307
|
log?.(`redirectMethod: ${redirectMethod}`);
|
|
307
308
|
|
|
309
|
+
if (rest.action === "login") {
|
|
310
|
+
rest.preRedirectHook?.();
|
|
311
|
+
}
|
|
312
|
+
|
|
308
313
|
return oidcClientTsUserManager
|
|
309
314
|
.signinRedirect({
|
|
310
315
|
state: stateData,
|