oidc-spa 8.2.0 → 8.2.1
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/OidcMetadata.d.ts +5 -0
- package/core/OidcMetadata.js +56 -0
- package/core/OidcMetadata.js.map +1 -1
- package/core/createOidc.d.ts +1 -1
- package/core/createOidc.js +170 -102
- package/core/createOidc.js.map +1 -1
- package/core/diagnostic.d.ts +0 -1
- package/core/diagnostic.js +18 -5
- package/core/diagnostic.js.map +1 -1
- package/core/loginOrGoToAuthServer.d.ts +0 -1
- package/core/loginOrGoToAuthServer.js +1 -16
- package/core/loginOrGoToAuthServer.js.map +1 -1
- package/core/loginSilent.d.ts +1 -2
- package/core/loginSilent.js +3 -21
- package/core/loginSilent.js.map +1 -1
- package/esm/core/OidcMetadata.d.ts +5 -0
- package/esm/core/OidcMetadata.js +54 -0
- package/esm/core/OidcMetadata.js.map +1 -1
- package/esm/core/createOidc.d.ts +1 -1
- package/esm/core/createOidc.js +170 -102
- package/esm/core/createOidc.js.map +1 -1
- package/esm/core/diagnostic.d.ts +0 -1
- package/esm/core/diagnostic.js +15 -1
- package/esm/core/diagnostic.js.map +1 -1
- package/esm/core/loginOrGoToAuthServer.d.ts +0 -1
- package/esm/core/loginOrGoToAuthServer.js +1 -16
- package/esm/core/loginOrGoToAuthServer.js.map +1 -1
- package/esm/core/loginSilent.d.ts +1 -2
- package/esm/core/loginSilent.js +3 -21
- package/esm/core/loginSilent.js.map +1 -1
- package/esm/keycloak/keycloakIssuerUriParsed.js +8 -1
- package/esm/keycloak/keycloakIssuerUriParsed.js.map +1 -1
- package/esm/tools/isLikelyDevServer.d.ts +1 -0
- package/esm/tools/isLikelyDevServer.js +14 -0
- package/esm/tools/isLikelyDevServer.js.map +1 -0
- package/keycloak/keycloakIssuerUriParsed.js +8 -1
- package/keycloak/keycloakIssuerUriParsed.js.map +1 -1
- package/package.json +1 -1
- package/src/core/OidcMetadata.ts +75 -0
- package/src/core/createOidc.ts +209 -137
- package/src/core/diagnostic.ts +21 -2
- package/src/core/loginOrGoToAuthServer.ts +0 -22
- package/src/core/loginSilent.ts +4 -27
- package/src/keycloak/keycloakIssuerUriParsed.ts +10 -1
- package/src/tools/isLikelyDevServer.ts +17 -0
- package/tools/isLikelyDevServer.d.ts +1 -0
- package/tools/isLikelyDevServer.js +17 -0
- package/tools/isLikelyDevServer.js.map +1 -0
package/src/core/createOidc.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type User as OidcClientTsUser,
|
|
5
5
|
InMemoryWebStorage
|
|
6
6
|
} from "../vendor/frontend/oidc-client-ts";
|
|
7
|
-
import type
|
|
7
|
+
import { type OidcMetadata, fetchOidcMetadata } from "./OidcMetadata";
|
|
8
8
|
import { assert, is, type Equals } from "../tools/tsafe/assert";
|
|
9
9
|
import { id } from "../tools/tsafe/id";
|
|
10
10
|
import { setTimeout, clearTimeout } from "../tools/workerTimers";
|
|
@@ -49,10 +49,10 @@ import { createGetIsNewBrowserSession } from "./isNewBrowserSession";
|
|
|
49
49
|
import { getIsOnline } from "../tools/getIsOnline";
|
|
50
50
|
import { isKeycloak } from "../keycloak/isKeycloak";
|
|
51
51
|
import { INFINITY_TIME } from "../tools/INFINITY_TIME";
|
|
52
|
-
import type { WELL_KNOWN_PATH } from "./diagnostic";
|
|
53
|
-
import { getIsValidRemoteJson } from "../tools/getIsValidRemoteJson";
|
|
54
52
|
import { prShouldLoadApp } from "./prShouldLoadApp";
|
|
55
53
|
import { getBASE_URL } from "./BASE_URL";
|
|
54
|
+
import { getIsLikelyDevServer } from "../tools/isLikelyDevServer";
|
|
55
|
+
import { createObjectThatThrowsIfAccessed } from "../tools/createObjectThatThrowsIfAccessed";
|
|
56
56
|
|
|
57
57
|
// NOTE: Replaced at build time
|
|
58
58
|
const VERSION = "{{OIDC_SPA_VERSION}}";
|
|
@@ -432,92 +432,212 @@ export async function createOidc_nonMemoized<
|
|
|
432
432
|
|
|
433
433
|
const stateUrlParamValue_instance = generateStateUrlParamValue();
|
|
434
434
|
|
|
435
|
+
const oidcMetadata = __metadata ?? (await fetchOidcMetadata({ issuerUri }));
|
|
436
|
+
|
|
435
437
|
const canUseIframe = (() => {
|
|
436
438
|
if (noIframe) {
|
|
437
439
|
return false;
|
|
438
440
|
}
|
|
439
441
|
|
|
440
442
|
third_party_cookies: {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
url1: window.location.origin,
|
|
444
|
-
url2: issuerUri
|
|
445
|
-
}) === false;
|
|
446
|
-
|
|
447
|
-
if (!isOidcServerThirdPartyRelativeToApp) {
|
|
448
|
-
break third_party_cookies;
|
|
443
|
+
if (oidcMetadata === undefined) {
|
|
444
|
+
return false;
|
|
449
445
|
}
|
|
450
446
|
|
|
451
|
-
const
|
|
452
|
-
const ua = navigator.userAgent;
|
|
453
|
-
const vendor = navigator.vendor;
|
|
447
|
+
const { authorization_endpoint } = oidcMetadata;
|
|
454
448
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
449
|
+
assert(
|
|
450
|
+
authorization_endpoint !== undefined,
|
|
451
|
+
"Missing authorization_endpoint on the provided __metadata"
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
const isOidcServerThirdPartyRelativeToApp = !getHaveSharedParentDomain({
|
|
455
|
+
url1: window.location.origin,
|
|
456
|
+
// TODO: No, here we should test against the authorization endpoint!
|
|
457
|
+
url2: authorization_endpoint
|
|
458
|
+
});
|
|
459
459
|
|
|
460
|
-
if (
|
|
460
|
+
if (!isOidcServerThirdPartyRelativeToApp) {
|
|
461
461
|
break third_party_cookies;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
-
|
|
465
|
-
[
|
|
466
|
-
"Can't use iframe because your auth server is on a third party domain relative",
|
|
467
|
-
"to the domain of your app and third party cookies are blocked by navigators."
|
|
468
|
-
].join(" ")
|
|
469
|
-
);
|
|
464
|
+
const isLikelyDevServer = getIsLikelyDevServer();
|
|
470
465
|
|
|
471
|
-
|
|
472
|
-
}
|
|
466
|
+
const domain_auth = new URL(authorization_endpoint).origin.split("//")[1];
|
|
473
467
|
|
|
474
|
-
|
|
475
|
-
return true;
|
|
476
|
-
})();
|
|
468
|
+
assert(domain_auth !== undefined, "33921384");
|
|
477
469
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
response_mode: isKeycloak({ issuerUri }) ? "fragment" : "query",
|
|
488
|
-
response_type: "code",
|
|
489
|
-
scope: Array.from(new Set(["openid", ...scopes])).join(" "),
|
|
490
|
-
automaticSilentRenew: false,
|
|
491
|
-
userStore: new WebStorageStateStore({
|
|
492
|
-
store: (() => {
|
|
493
|
-
if (canUseIframe) {
|
|
494
|
-
isUserStoreInMemoryOnly = true;
|
|
495
|
-
return new InMemoryWebStorage();
|
|
470
|
+
const domain_here = window.location.origin.split("//")[1];
|
|
471
|
+
|
|
472
|
+
let isWellKnownProviderDomain = false;
|
|
473
|
+
let isIp = false;
|
|
474
|
+
|
|
475
|
+
const suggestedDeployments = (() => {
|
|
476
|
+
if (/^(?:\d{1,3}\.){3}\d{1,3}$|^\[?[A-Fa-f0-9:]+\]?$/.test(domain_auth)) {
|
|
477
|
+
isIp = true;
|
|
478
|
+
return [];
|
|
496
479
|
}
|
|
497
480
|
|
|
498
|
-
|
|
481
|
+
const baseDomain = (() => {
|
|
482
|
+
const segments = domain_auth.split(".");
|
|
499
483
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
484
|
+
if (segments.length >= 3) {
|
|
485
|
+
segments.shift();
|
|
486
|
+
}
|
|
487
|
+
return segments.join(".");
|
|
488
|
+
})();
|
|
503
489
|
|
|
504
|
-
|
|
490
|
+
{
|
|
491
|
+
const baseDomain_low = baseDomain.toLowerCase();
|
|
505
492
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
493
|
+
if (
|
|
494
|
+
baseDomain_low.includes("auth0") ||
|
|
495
|
+
baseDomain_low.includes("clerk") ||
|
|
496
|
+
baseDomain_low.includes("microsoft") ||
|
|
497
|
+
baseDomain_low.includes("okta") ||
|
|
498
|
+
baseDomain_low.includes("aws")
|
|
499
|
+
) {
|
|
500
|
+
isWellKnownProviderDomain = true;
|
|
501
|
+
return [];
|
|
509
502
|
}
|
|
503
|
+
}
|
|
510
504
|
|
|
511
|
-
|
|
512
|
-
});
|
|
505
|
+
const baseUrl = new URL(homeUrlAndRedirectUri).pathname;
|
|
513
506
|
|
|
514
|
-
return
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
507
|
+
return [
|
|
508
|
+
`myapp.${baseDomain}`,
|
|
509
|
+
baseDomain === domain_auth ? undefined : baseDomain,
|
|
510
|
+
`${baseDomain}/${baseUrl === "/" ? "dashboard" : baseUrl}`
|
|
511
|
+
].filter(x => x !== undefined);
|
|
512
|
+
})();
|
|
513
|
+
|
|
514
|
+
if (isLikelyDevServer) {
|
|
515
|
+
log?.(
|
|
516
|
+
[
|
|
517
|
+
"Detected localhost environment.",
|
|
518
|
+
"\nWhen reloading while logged in, you may briefly see",
|
|
519
|
+
"some URL params appear in the address bar.",
|
|
520
|
+
"\nThis happens because session restore via iframe is disabled,",
|
|
521
|
+
"the browser treats your auth server as a third party.",
|
|
522
|
+
`\nAuth server: ${domain_auth}`,
|
|
523
|
+
`\nApp domain: ${domain_here}`,
|
|
524
|
+
...(() => {
|
|
525
|
+
if (isIp) {
|
|
526
|
+
return [];
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (isWellKnownProviderDomain) {
|
|
530
|
+
return [
|
|
531
|
+
"\nYou seem to be using a well-known auth provider.",
|
|
532
|
+
"Check your provider's docs, some allow configuring",
|
|
533
|
+
`a your custom domain at least for the authorization endpoint.`,
|
|
534
|
+
"\nIf configured, oidc-spa will restore sessions silently",
|
|
535
|
+
"and improve the user experience."
|
|
536
|
+
];
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return [
|
|
540
|
+
"\nOnce deployed under the same root domain as your auth server,",
|
|
541
|
+
"oidc-spa will use iframes to restore sessions silently.",
|
|
542
|
+
"\nSuggested deployments:",
|
|
543
|
+
...suggestedDeployments.map(d => `\n • ${d}`)
|
|
544
|
+
];
|
|
545
|
+
})(),
|
|
546
|
+
"\n\nMore info:",
|
|
547
|
+
"https://docs.oidc-spa.dev/v/v8/resources/end-of-third-party-cookies#when-are-cookies-considered-third-party"
|
|
548
|
+
].join(" ")
|
|
549
|
+
);
|
|
550
|
+
} else {
|
|
551
|
+
log?.(
|
|
552
|
+
[
|
|
553
|
+
"Silent session restore via iframe is disabled.",
|
|
554
|
+
`\nAuth server: ${domain_auth}`,
|
|
555
|
+
`App domain: ${domain_here}`,
|
|
556
|
+
"\nThey do not share a common root domain.",
|
|
557
|
+
...(() => {
|
|
558
|
+
if (isIp) {
|
|
559
|
+
return [];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (isWellKnownProviderDomain) {
|
|
563
|
+
return [
|
|
564
|
+
"\nYou seem to be using a well-known auth provider.",
|
|
565
|
+
"Check if you can configure a custom auth domain.",
|
|
566
|
+
"\nIf so, oidc-spa can restore sessions silently",
|
|
567
|
+
"and improve the user experience."
|
|
568
|
+
];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return [
|
|
572
|
+
"\nTo improve the experience, here are some examples of deployment for your app:",
|
|
573
|
+
...suggestedDeployments.map(d => `\n • ${d}`)
|
|
574
|
+
];
|
|
575
|
+
})(),
|
|
576
|
+
"\nMore info:",
|
|
577
|
+
"https://docs.oidc-spa.dev/v/v8/resources/end-of-third-party-cookies#when-are-cookies-considered-third-party"
|
|
578
|
+
].join(" ")
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return true;
|
|
586
|
+
})();
|
|
587
|
+
|
|
588
|
+
let isUserStoreInMemoryOnly: boolean | undefined = undefined;
|
|
589
|
+
|
|
590
|
+
const oidcClientTsUserManager =
|
|
591
|
+
oidcMetadata === undefined
|
|
592
|
+
? createObjectThatThrowsIfAccessed<OidcClientTsUserManager>({
|
|
593
|
+
debugMessage: "oidc-spa: Wrong assertion 43943"
|
|
594
|
+
})
|
|
595
|
+
: new OidcClientTsUserManager({
|
|
596
|
+
stateUrlParamValue: stateUrlParamValue_instance,
|
|
597
|
+
authority: issuerUri,
|
|
598
|
+
client_id: clientId,
|
|
599
|
+
redirect_uri: homeUrlAndRedirectUri,
|
|
600
|
+
silent_redirect_uri: homeUrlAndRedirectUri,
|
|
601
|
+
post_logout_redirect_uri: homeUrlAndRedirectUri,
|
|
602
|
+
response_mode: isKeycloak({ issuerUri }) ? "fragment" : "query",
|
|
603
|
+
response_type: "code",
|
|
604
|
+
scope: Array.from(new Set(["openid", ...scopes])).join(" "),
|
|
605
|
+
automaticSilentRenew: false,
|
|
606
|
+
userStore: new WebStorageStateStore({
|
|
607
|
+
store: (() => {
|
|
608
|
+
if (canUseIframe) {
|
|
609
|
+
isUserStoreInMemoryOnly = true;
|
|
610
|
+
return new InMemoryWebStorage();
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
isUserStoreInMemoryOnly = false;
|
|
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
|
+
}
|
|
626
|
+
|
|
627
|
+
storage.persistCurrentStateAndSubsequentChanges();
|
|
628
|
+
}
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
return storage;
|
|
632
|
+
})()
|
|
633
|
+
}),
|
|
634
|
+
stateStore: new WebStorageStateStore({
|
|
635
|
+
store: localStorage,
|
|
636
|
+
prefix: STATE_STORE_KEY_PREFIX
|
|
637
|
+
}),
|
|
638
|
+
client_secret: __unsafe_clientSecret,
|
|
639
|
+
metadata: oidcMetadata
|
|
640
|
+
});
|
|
521
641
|
|
|
522
642
|
const evtInitializationOutcomeUserNotLoggedIn = createEvt<void>();
|
|
523
643
|
|
|
@@ -547,6 +667,14 @@ export async function createOidc_nonMemoized<
|
|
|
547
667
|
backFromAuthServer: Oidc.LoggedIn["backFromAuthServer"]; // Undefined is silent signin
|
|
548
668
|
}
|
|
549
669
|
> => {
|
|
670
|
+
if (oidcMetadata === undefined) {
|
|
671
|
+
return (
|
|
672
|
+
await import("./diagnostic")
|
|
673
|
+
).createWellKnownOidcConfigurationEndpointUnreachableInitializationError({
|
|
674
|
+
issuerUri
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
550
678
|
handle_redirect_auth_response: {
|
|
551
679
|
let stateDataAndAuthResponse:
|
|
552
680
|
| { stateData: StateData.Redirect; authResponse: AuthResponse }
|
|
@@ -731,6 +859,8 @@ export async function createOidc_nonMemoized<
|
|
|
731
859
|
// NOTE: We almost never persist tokens, we have to only to support edge case
|
|
732
860
|
// of multiple oidc instance in a single App with no iframe support.
|
|
733
861
|
restore_from_session_storage: {
|
|
862
|
+
assert(isUserStoreInMemoryOnly !== undefined, "3392204");
|
|
863
|
+
|
|
734
864
|
if (isUserStoreInMemoryOnly) {
|
|
735
865
|
break restore_from_session_storage;
|
|
736
866
|
}
|
|
@@ -801,20 +931,6 @@ export async function createOidc_nonMemoized<
|
|
|
801
931
|
}
|
|
802
932
|
|
|
803
933
|
if (!canUseIframe) {
|
|
804
|
-
if (
|
|
805
|
-
!(await getIsValidRemoteJson(
|
|
806
|
-
`${issuerUri}${id<typeof WELL_KNOWN_PATH>(
|
|
807
|
-
"/.well-known/openid-configuration"
|
|
808
|
-
)}`
|
|
809
|
-
))
|
|
810
|
-
) {
|
|
811
|
-
return (
|
|
812
|
-
await import("./diagnostic")
|
|
813
|
-
).createWellKnownOidcConfigurationEndpointUnreachableInitializationError({
|
|
814
|
-
issuerUri
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
|
|
818
934
|
break actual_silent_signin;
|
|
819
935
|
}
|
|
820
936
|
|
|
@@ -835,26 +951,13 @@ export async function createOidc_nonMemoized<
|
|
|
835
951
|
|
|
836
952
|
assert(result_loginSilent.outcome !== "token refreshed using refresh token", "876995");
|
|
837
953
|
|
|
838
|
-
if (result_loginSilent.outcome === "
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
});
|
|
846
|
-
case "timeout":
|
|
847
|
-
return (await import("./diagnostic")).createIframeTimeoutInitializationError(
|
|
848
|
-
{
|
|
849
|
-
redirectUri: homeUrlAndRedirectUri,
|
|
850
|
-
clientId,
|
|
851
|
-
issuerUri,
|
|
852
|
-
noIframe
|
|
853
|
-
}
|
|
854
|
-
);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
assert<Equals<typeof result_loginSilent.cause, never>>(false);
|
|
954
|
+
if (result_loginSilent.outcome === "timeout") {
|
|
955
|
+
return (await import("./diagnostic")).createIframeTimeoutInitializationError({
|
|
956
|
+
redirectUri: homeUrlAndRedirectUri,
|
|
957
|
+
clientId,
|
|
958
|
+
issuerUri,
|
|
959
|
+
noIframe
|
|
960
|
+
});
|
|
858
961
|
}
|
|
859
962
|
|
|
860
963
|
assert<Equals<typeof result_loginSilent.outcome, "got auth response from iframe">>();
|
|
@@ -918,9 +1021,7 @@ export async function createOidc_nonMemoized<
|
|
|
918
1021
|
});
|
|
919
1022
|
}
|
|
920
1023
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
loginOrGoToAuthServer({
|
|
1024
|
+
await loginOrGoToAuthServer({
|
|
924
1025
|
action: "login",
|
|
925
1026
|
doForceReloadOnBfCache: true,
|
|
926
1027
|
redirectUrl: getRootRelativeOriginalLocationHref(),
|
|
@@ -939,19 +1040,7 @@ export async function createOidc_nonMemoized<
|
|
|
939
1040
|
}
|
|
940
1041
|
|
|
941
1042
|
return "ensure no interaction";
|
|
942
|
-
})()
|
|
943
|
-
onCantFetchWellKnownEndpointError: () => {
|
|
944
|
-
dCantFetchWellKnownEndpointOrNever.resolve();
|
|
945
|
-
}
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
await dCantFetchWellKnownEndpointOrNever.pr;
|
|
949
|
-
|
|
950
|
-
return (
|
|
951
|
-
await import("./diagnostic")
|
|
952
|
-
).createFailedToFetchTokenEndpointInitializationError({
|
|
953
|
-
clientId,
|
|
954
|
-
issuerUri
|
|
1043
|
+
})()
|
|
955
1044
|
});
|
|
956
1045
|
}
|
|
957
1046
|
|
|
@@ -1072,11 +1161,7 @@ export async function createOidc_nonMemoized<
|
|
|
1072
1161
|
interaction:
|
|
1073
1162
|
getPersistedAuthState({ configId }) === "explicitly logged out"
|
|
1074
1163
|
? "ensure interaction"
|
|
1075
|
-
: "directly redirect if active session show login otherwise"
|
|
1076
|
-
onCantFetchWellKnownEndpointError: () => {
|
|
1077
|
-
log?.("Login called but the auth server seems to be down..");
|
|
1078
|
-
alert("Authentication unavailable please try again later.");
|
|
1079
|
-
}
|
|
1164
|
+
: "directly redirect if active session show login otherwise"
|
|
1080
1165
|
});
|
|
1081
1166
|
},
|
|
1082
1167
|
initializationError: undefined
|
|
@@ -1308,16 +1393,7 @@ export async function createOidc_nonMemoized<
|
|
|
1308
1393
|
extraQueryParams_local: undefined,
|
|
1309
1394
|
transformUrlBeforeRedirect_local: undefined,
|
|
1310
1395
|
doNavigateBackToLastPublicUrlIfTheTheUserNavigateBack: false,
|
|
1311
|
-
interaction: "directly redirect if active session show login otherwise"
|
|
1312
|
-
onCantFetchWellKnownEndpointError: () => {
|
|
1313
|
-
log?.(
|
|
1314
|
-
[
|
|
1315
|
-
"The auth server seems to be down while we needed to refresh the token",
|
|
1316
|
-
"with a full page redirect. Reloading the page"
|
|
1317
|
-
].join(" ")
|
|
1318
|
-
);
|
|
1319
|
-
window.location.reload();
|
|
1320
|
-
}
|
|
1396
|
+
interaction: "directly redirect if active session show login otherwise"
|
|
1321
1397
|
});
|
|
1322
1398
|
assert(false, "136134");
|
|
1323
1399
|
};
|
|
@@ -1352,10 +1428,10 @@ export async function createOidc_nonMemoized<
|
|
|
1352
1428
|
log
|
|
1353
1429
|
});
|
|
1354
1430
|
|
|
1355
|
-
if (result_loginSilent.outcome === "
|
|
1431
|
+
if (result_loginSilent.outcome === "timeout") {
|
|
1356
1432
|
log?.(
|
|
1357
1433
|
[
|
|
1358
|
-
`Silent refresh of the token failed
|
|
1434
|
+
`Silent refresh of the token failed the iframe didn't post a response (timeout).`,
|
|
1359
1435
|
`This isn't recoverable, reloading the page.`
|
|
1360
1436
|
].join(" ")
|
|
1361
1437
|
);
|
|
@@ -1545,11 +1621,7 @@ export async function createOidc_nonMemoized<
|
|
|
1545
1621
|
action: "go to auth server",
|
|
1546
1622
|
redirectUrl: redirectUrl ?? window.location.href,
|
|
1547
1623
|
extraQueryParams_local: extraQueryParams,
|
|
1548
|
-
transformUrlBeforeRedirect_local: transformUrlBeforeRedirect
|
|
1549
|
-
onCantFetchWellKnownEndpointError: () => {
|
|
1550
|
-
log?.("goToAuthServer called but the auth server seems to be down..");
|
|
1551
|
-
alert("Authentication unavailable please try again later.");
|
|
1552
|
-
}
|
|
1624
|
+
transformUrlBeforeRedirect_local: transformUrlBeforeRedirect
|
|
1553
1625
|
}),
|
|
1554
1626
|
backFromAuthServer: resultOfLoginProcess.backFromAuthServer,
|
|
1555
1627
|
isNewBrowserSession: (() => {
|
package/src/core/diagnostic.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { OidcInitializationError } from "./OidcInitializationError";
|
|
2
2
|
import { isKeycloak, createKeycloakUtils } from "../keycloak";
|
|
3
3
|
import { getIsValidRemoteJson } from "../tools/getIsValidRemoteJson";
|
|
4
|
-
|
|
5
|
-
export const WELL_KNOWN_PATH = "/.well-known/openid-configuration";
|
|
4
|
+
import { WELL_KNOWN_PATH } from "./OidcMetadata";
|
|
6
5
|
|
|
7
6
|
export async function createWellKnownOidcConfigurationEndpointUnreachableInitializationError(params: {
|
|
8
7
|
issuerUri: string;
|
|
@@ -95,6 +94,16 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
95
94
|
}): Promise<OidcInitializationError> {
|
|
96
95
|
const { redirectUri, issuerUri, clientId, noIframe } = params;
|
|
97
96
|
|
|
97
|
+
check_if_well_known_endpoint_is_reachable: {
|
|
98
|
+
const isValid = await getIsValidRemoteJson(`${issuerUri}${WELL_KNOWN_PATH}`);
|
|
99
|
+
|
|
100
|
+
if (isValid) {
|
|
101
|
+
break check_if_well_known_endpoint_is_reachable;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return createWellKnownOidcConfigurationEndpointUnreachableInitializationError({ issuerUri });
|
|
105
|
+
}
|
|
106
|
+
|
|
98
107
|
iframe_blocked: {
|
|
99
108
|
if (noIframe) {
|
|
100
109
|
break iframe_blocked;
|
|
@@ -233,6 +242,16 @@ export async function createFailedToFetchTokenEndpointInitializationError(params
|
|
|
233
242
|
}) {
|
|
234
243
|
const { issuerUri, clientId } = params;
|
|
235
244
|
|
|
245
|
+
check_if_well_known_endpoint_is_reachable: {
|
|
246
|
+
const isValid = await getIsValidRemoteJson(`${issuerUri}${WELL_KNOWN_PATH}`);
|
|
247
|
+
|
|
248
|
+
if (isValid) {
|
|
249
|
+
break check_if_well_known_endpoint_is_reachable;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return createWellKnownOidcConfigurationEndpointUnreachableInitializationError({ issuerUri });
|
|
253
|
+
}
|
|
254
|
+
|
|
236
255
|
return new OidcInitializationError({
|
|
237
256
|
isAuthServerLikelyDown: false,
|
|
238
257
|
messageOrCause: [
|
|
@@ -20,7 +20,6 @@ namespace Params {
|
|
|
20
20
|
redirectUrl: string;
|
|
21
21
|
extraQueryParams_local: Record<string, string | undefined> | undefined;
|
|
22
22
|
transformUrlBeforeRedirect_local: ((url: string) => string) | undefined;
|
|
23
|
-
onCantFetchWellKnownEndpointError: () => void;
|
|
24
23
|
};
|
|
25
24
|
|
|
26
25
|
export type Login = Common & {
|
|
@@ -90,10 +89,8 @@ export function createLoginOrGoToAuthServer(params: {
|
|
|
90
89
|
redirectUrl: redirectUrl_params,
|
|
91
90
|
extraQueryParams_local,
|
|
92
91
|
transformUrlBeforeRedirect_local,
|
|
93
|
-
onCantFetchWellKnownEndpointError: onCantFetchWellKnownEndpointError_params,
|
|
94
92
|
...rest
|
|
95
93
|
} = params;
|
|
96
|
-
let onCantFetchWellKnownEndpointError = onCantFetchWellKnownEndpointError_params;
|
|
97
94
|
|
|
98
95
|
log?.(`Calling loginOrGoToAuthServer ${JSON.stringify(params, null, 2)}`);
|
|
99
96
|
|
|
@@ -147,12 +144,6 @@ export function createLoginOrGoToAuthServer(params: {
|
|
|
147
144
|
};
|
|
148
145
|
|
|
149
146
|
window.addEventListener("pageshow", callback);
|
|
150
|
-
|
|
151
|
-
onCantFetchWellKnownEndpointError = () => {
|
|
152
|
-
window.removeEventListener("pageshow", callback);
|
|
153
|
-
onCantFetchWellKnownEndpointError_params();
|
|
154
|
-
};
|
|
155
|
-
|
|
156
147
|
break bf_cache_handling;
|
|
157
148
|
}
|
|
158
149
|
|
|
@@ -183,12 +174,6 @@ export function createLoginOrGoToAuthServer(params: {
|
|
|
183
174
|
};
|
|
184
175
|
|
|
185
176
|
window.addEventListener("pageshow", callback);
|
|
186
|
-
|
|
187
|
-
onCantFetchWellKnownEndpointError = () => {
|
|
188
|
-
window.removeEventListener("pageshow", callback);
|
|
189
|
-
globalContext.evtHasLoginBeenCalled.current = false;
|
|
190
|
-
onCantFetchWellKnownEndpointError_params();
|
|
191
|
-
};
|
|
192
177
|
}
|
|
193
178
|
}
|
|
194
179
|
|
|
@@ -348,13 +333,6 @@ export function createLoginOrGoToAuthServer(params: {
|
|
|
348
333
|
.then(
|
|
349
334
|
() => new Promise<never>(() => {}),
|
|
350
335
|
(error: Error) => {
|
|
351
|
-
if (error.message === "Failed to fetch") {
|
|
352
|
-
// NOTE: See ./loginSilent for explanation.
|
|
353
|
-
onCantFetchWellKnownEndpointError();
|
|
354
|
-
|
|
355
|
-
return new Promise<never>(() => {});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
336
|
if (error.message.includes("Crypto.subtle is available only in secure contexts")) {
|
|
359
337
|
throw new Error(
|
|
360
338
|
[
|
package/src/core/loginSilent.ts
CHANGED
|
@@ -20,8 +20,7 @@ type ResultOfLoginSilent =
|
|
|
20
20
|
authResponse: AuthResponse;
|
|
21
21
|
}
|
|
22
22
|
| {
|
|
23
|
-
outcome: "
|
|
24
|
-
cause: "timeout" | "can't reach well-known oidc endpoint";
|
|
23
|
+
outcome: "timeout";
|
|
25
24
|
}
|
|
26
25
|
| {
|
|
27
26
|
outcome: "token refreshed using refresh token";
|
|
@@ -106,8 +105,7 @@ export async function loginSilent(params: {
|
|
|
106
105
|
const timeouts = [
|
|
107
106
|
setTimeout(() => {
|
|
108
107
|
dResult.resolve({
|
|
109
|
-
outcome: "
|
|
110
|
-
cause: "timeout"
|
|
108
|
+
outcome: "timeout"
|
|
111
109
|
});
|
|
112
110
|
}, timeoutDelayMs),
|
|
113
111
|
setTimeout(() => {
|
|
@@ -259,28 +257,7 @@ export async function loginSilent(params: {
|
|
|
259
257
|
oidcClientTsUser
|
|
260
258
|
});
|
|
261
259
|
},
|
|
262
|
-
(
|
|
263
|
-
if (error.message === "Failed to fetch") {
|
|
264
|
-
// NOTE: If we got an error here it means that the fetch to the
|
|
265
|
-
// well-known oidc endpoint failed.
|
|
266
|
-
// This usually means that the server is down or that the issuerUri
|
|
267
|
-
// is not pointing to a valid oidc server.
|
|
268
|
-
// It could be a CORS error on the well-known endpoint but it's unlikely.
|
|
269
|
-
|
|
270
|
-
// NOTE: This error should happen well before we displayed
|
|
271
|
-
// the warning notifying that something is probably misconfigured.
|
|
272
|
-
// wasSuccess shouldn't really be a required parameter but we do it
|
|
273
|
-
// for peace of mind.
|
|
274
|
-
clearTimeouts({ wasSuccess: false });
|
|
275
|
-
|
|
276
|
-
dResult.resolve({
|
|
277
|
-
outcome: "failure",
|
|
278
|
-
cause: "can't reach well-known oidc endpoint"
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
260
|
+
() => {
|
|
284
261
|
// NOTE: Here, except error on our understanding there can't be any other
|
|
285
262
|
// error than timeout so we fail silently and let the timeout expire.
|
|
286
263
|
}
|
|
@@ -289,7 +266,7 @@ export async function loginSilent(params: {
|
|
|
289
266
|
dResult.pr.then(result => {
|
|
290
267
|
clearSessionStoragePublicKey();
|
|
291
268
|
|
|
292
|
-
if (result.outcome === "
|
|
269
|
+
if (result.outcome === "timeout") {
|
|
293
270
|
clearStateStore({ stateUrlParamValue: stateUrlParamValue_instance });
|
|
294
271
|
}
|
|
295
272
|
});
|
|
@@ -11,7 +11,16 @@ export type KeycloakIssuerUriParsed = {
|
|
|
11
11
|
export function parseKeycloakIssuerUri(params: { issuerUri: string }): KeycloakIssuerUriParsed {
|
|
12
12
|
const { issuerUri } = params;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
if (!isKeycloak({ issuerUri })) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
[
|
|
17
|
+
`oidc-spa: The issuer uri provided ${issuerUri}`,
|
|
18
|
+
"if you are in an environnement that should support multiple",
|
|
19
|
+
"auth provider, you should first test `isKeycloakUrl({ issuerUri })`",
|
|
20
|
+
"before calling parseKeycloakIssuerUri({ issuerUri })"
|
|
21
|
+
].join(" ")
|
|
22
|
+
);
|
|
23
|
+
}
|
|
15
24
|
|
|
16
25
|
const url = new URL(issuerUri.replace(/\/$/, ""));
|
|
17
26
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function getIsLikelyDevServer(): boolean {
|
|
2
|
+
const origin = window.location.origin;
|
|
3
|
+
|
|
4
|
+
if (/^https?:\/\/localhost/.test(origin)) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (/^https?:\/\/\[::\]/.test(origin)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (/^https?:\/\/127.0.0.1/.test(origin)) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getIsLikelyDevServer(): boolean;
|