oidc-spa 7.1.9 → 7.2.0-rc.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/backend.js +235 -352
- package/backend.js.map +1 -1
- package/core/AuthResponse.js +12 -49
- package/core/AuthResponse.js.map +1 -1
- package/core/Oidc.d.ts +1 -2
- package/core/OidcInitializationError.d.ts +2 -2
- package/core/OidcInitializationError.js +230 -297
- package/core/OidcInitializationError.js.map +1 -1
- package/core/OidcMetadata.js +1 -1
- package/core/OidcMetadata.js.map +1 -1
- package/core/StateData.d.ts +5 -5
- package/core/StateData.js +25 -25
- package/core/StateData.js.map +1 -1
- package/core/configId.js +1 -1
- package/core/configId.js.map +1 -1
- package/core/createOidc.d.ts +8 -0
- package/core/createOidc.js +999 -1294
- package/core/createOidc.js.map +1 -1
- package/core/evtIsUserActive.js +26 -27
- package/core/evtIsUserActive.js.map +1 -1
- package/core/handleOidcCallback.js +99 -154
- package/core/handleOidcCallback.js.map +1 -1
- package/core/iframeMessageProtection.d.ts +1 -1
- package/core/iframeMessageProtection.js +43 -108
- package/core/iframeMessageProtection.js.map +1 -1
- package/core/index.d.ts +1 -1
- package/core/index.js +3 -3
- package/core/index.js.map +1 -1
- package/core/initialLocationHref.js +1 -1
- package/core/initialLocationHref.js.map +1 -1
- package/core/isNewBrowserSession.js +8 -8
- package/core/isNewBrowserSession.js.map +1 -1
- package/core/loginOrGoToAuthServer.d.ts +1 -1
- package/core/loginOrGoToAuthServer.js +188 -310
- package/core/loginOrGoToAuthServer.js.map +1 -1
- package/core/loginPropagationToOtherTabs.js +15 -16
- package/core/loginPropagationToOtherTabs.js.map +1 -1
- package/core/loginSilent.d.ts +2 -3
- package/core/loginSilent.js +118 -214
- package/core/loginSilent.js.map +1 -1
- package/core/logoutPropagationToOtherTabs.js +15 -16
- package/core/logoutPropagationToOtherTabs.js.map +1 -1
- package/core/oidcClientTsUserToTokens.d.ts +1 -1
- package/core/oidcClientTsUserToTokens.js +75 -72
- package/core/oidcClientTsUserToTokens.js.map +1 -1
- package/core/ongoingLoginOrRefreshProcesses.js +23 -89
- package/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
- package/core/persistedAuthState.js +13 -13
- package/core/persistedAuthState.js.map +1 -1
- package/entrypoint.js +9 -9
- package/entrypoint.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -2
- package/index.js.map +1 -1
- package/keycloak/index.d.ts +3 -0
- package/keycloak/index.js +8 -0
- package/keycloak/index.js.map +1 -0
- package/keycloak/isKeycloak.d.ts +3 -0
- package/keycloak/isKeycloak.js +20 -0
- package/keycloak/isKeycloak.js.map +1 -0
- package/keycloak/keycloak-js/Keycloak.d.ts +284 -0
- package/keycloak/keycloak-js/Keycloak.js +778 -0
- package/keycloak/keycloak-js/Keycloak.js.map +1 -0
- package/keycloak/keycloak-js/index.d.ts +2 -0
- package/keycloak/keycloak-js/index.js +6 -0
- package/keycloak/keycloak-js/index.js.map +1 -0
- package/keycloak/keycloak-js/types.d.ts +361 -0
- package/keycloak/keycloak-js/types.js +3 -0
- package/keycloak/keycloak-js/types.js.map +1 -0
- package/keycloak/keycloakIssuerUriParsed.d.ts +9 -0
- package/keycloak/keycloakIssuerUriParsed.js +19 -0
- package/keycloak/keycloakIssuerUriParsed.js.map +1 -0
- package/keycloak/keycloakUtils.d.ts +37 -0
- package/keycloak/keycloakUtils.js +47 -0
- package/keycloak/keycloakUtils.js.map +1 -0
- package/keycloak-js.d.ts +1 -0
- package/keycloak-js.js +18 -0
- package/keycloak-js.js.map +1 -0
- package/mock/oidc.js +147 -194
- package/mock/oidc.js.map +1 -1
- package/mock/react.js +2 -2
- package/mock/react.js.map +1 -1
- package/package.json +38 -9
- package/react/react.js +133 -244
- package/react/react.js.map +1 -1
- package/src/core/AuthResponse.ts +2 -0
- package/src/core/Oidc.ts +1 -2
- package/src/core/OidcInitializationError.ts +30 -30
- package/src/core/OidcMetadata.ts +1 -1
- package/src/core/StateData.ts +24 -24
- package/src/core/createOidc.ts +24 -31
- package/src/core/handleOidcCallback.ts +44 -23
- package/src/core/iframeMessageProtection.ts +11 -10
- package/src/core/index.ts +1 -1
- package/src/core/loginOrGoToAuthServer.ts +1 -1
- package/src/core/loginSilent.ts +14 -11
- package/src/core/oidcClientTsUserToTokens.ts +1 -1
- package/src/index.ts +1 -7
- package/src/keycloak/index.ts +8 -0
- package/src/keycloak/isKeycloak.ts +23 -0
- package/src/keycloak/keycloak-js/Keycloak.ts +1097 -0
- package/src/keycloak/keycloak-js/index.ts +2 -0
- package/src/keycloak/keycloak-js/types.ts +442 -0
- package/src/keycloak/keycloakIssuerUriParsed.ts +29 -0
- package/src/keycloak/keycloakUtils.ts +90 -0
- package/src/keycloak-js.ts +1 -0
- package/src/react/react.tsx +17 -1
- package/src/tools/decodeJwt.ts +95 -2
- package/src/tools/parseKeycloakIssuerUri.ts +11 -30
- package/src/vendor/frontend/oidc-client-ts.ts +1 -0
- package/src/vendor/frontend/tsafe.ts +1 -0
- package/tools/Deferred.js +13 -35
- package/tools/Deferred.js.map +1 -1
- package/tools/EphemeralSessionStorage.js +46 -48
- package/tools/EphemeralSessionStorage.js.map +1 -1
- package/tools/Evt.js +14 -14
- package/tools/Evt.js.map +1 -1
- package/tools/StatefulEvt.js +5 -5
- package/tools/StatefulEvt.js.map +1 -1
- package/tools/asymmetricEncryption.js +81 -172
- package/tools/asymmetricEncryption.js.map +1 -1
- package/tools/base64.js +2 -2
- package/tools/base64.js.map +1 -1
- package/tools/createObjectThatThrowsIfAccessed.js +13 -61
- package/tools/createObjectThatThrowsIfAccessed.js.map +1 -1
- package/tools/decodeJwt.d.ts +25 -2
- package/tools/decodeJwt.js +61 -3
- package/tools/decodeJwt.js.map +1 -1
- package/tools/generateUrlSafeRandom.js +5 -30
- package/tools/generateUrlSafeRandom.js.map +1 -1
- package/tools/getDownlinkAndRtt.js +8 -30
- package/tools/getDownlinkAndRtt.js.map +1 -1
- package/tools/getIsOnline.js +3 -3
- package/tools/getIsOnline.js.map +1 -1
- package/tools/getIsValidRemoteJson.js +12 -59
- package/tools/getIsValidRemoteJson.js.map +1 -1
- package/tools/getPrUserInteraction.js +4 -4
- package/tools/getPrUserInteraction.js.map +1 -1
- package/tools/getUserEnvironmentInfo.js +17 -12
- package/tools/getUserEnvironmentInfo.js.map +1 -1
- package/tools/haveSharedParentDomain.js +5 -5
- package/tools/haveSharedParentDomain.js.map +1 -1
- package/tools/isDev.js +2 -2
- package/tools/isDev.js.map +1 -1
- package/tools/parseKeycloakIssuerUri.d.ts +2 -0
- package/tools/parseKeycloakIssuerUri.js +11 -42
- package/tools/parseKeycloakIssuerUri.js.map +1 -1
- package/tools/readExpirationTimeInJwt.js +4 -4
- package/tools/readExpirationTimeInJwt.js.map +1 -1
- package/tools/startCountdown.js +17 -65
- package/tools/startCountdown.js.map +1 -1
- package/tools/subscribeToUserInteraction.js +17 -66
- package/tools/subscribeToUserInteraction.js.map +1 -1
- package/tools/toFullyQualifiedUrl.js +7 -7
- package/tools/toFullyQualifiedUrl.js.map +1 -1
- package/tools/toHumanReadableDuration.js +13 -13
- package/tools/toHumanReadableDuration.js.map +1 -1
- package/tools/urlSearchParams.js +28 -50
- package/tools/urlSearchParams.js.map +1 -1
- package/tools/workerTimers.js +10 -10
- package/tools/workerTimers.js.map +1 -1
- package/vendor/frontend/oidc-client-ts.d.ts +1 -0
- package/vendor/frontend/oidc-client-ts.js +3686 -0
- package/vendor/frontend/tsafe.d.ts +1 -0
- package/vendor/frontend/tsafe.js +1 -1
- package/core/trustedFetch.d.ts +0 -2
- package/core/trustedFetch.js +0 -12
- package/core/trustedFetch.js.map +0 -1
- package/src/core/trustedFetch.ts +0 -9
- package/src/vendor/frontend/oidc-client-ts-and-jwt-decode.ts +0 -4
- package/vendor/frontend/oidc-client-ts-and-jwt-decode.d.ts +0 -3
- package/vendor/frontend/oidc-client-ts-and-jwt-decode.js +0 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getIsValidRemoteJson } from "../tools/getIsValidRemoteJson";
|
|
2
|
-
import {
|
|
2
|
+
import { isKeycloak } from "../keycloak/isKeycloak";
|
|
3
3
|
|
|
4
4
|
export class OidcInitializationError extends Error {
|
|
5
5
|
public readonly isAuthServerLikelyDown: boolean;
|
|
@@ -26,8 +26,6 @@ export async function createWellKnownOidcConfigurationEndpointUnreachableInitial
|
|
|
26
26
|
}): Promise<OidcInitializationError> {
|
|
27
27
|
const { issuerUri } = params;
|
|
28
28
|
|
|
29
|
-
const issuerUri_parsed = parseKeycloakIssuerUri(issuerUri);
|
|
30
|
-
|
|
31
29
|
const WELL_KNOWN_PATH = "/.well-known/openid-configuration";
|
|
32
30
|
|
|
33
31
|
const commonFallbackMessagePart = [
|
|
@@ -36,7 +34,7 @@ export async function createWellKnownOidcConfigurationEndpointUnreachableInitial
|
|
|
36
34
|
`Endpoint that couldn't be reached: ${issuerUri}${WELL_KNOWN_PATH}`
|
|
37
35
|
].join("\n");
|
|
38
36
|
|
|
39
|
-
if (
|
|
37
|
+
if (!isKeycloak({ issuerUri })) {
|
|
40
38
|
return new OidcInitializationError({
|
|
41
39
|
messageOrCause: [
|
|
42
40
|
commonFallbackMessagePart,
|
|
@@ -49,15 +47,17 @@ export async function createWellKnownOidcConfigurationEndpointUnreachableInitial
|
|
|
49
47
|
});
|
|
50
48
|
}
|
|
51
49
|
|
|
50
|
+
const keycloakUtils = (await import("../keycloak")).createKeycloakUtils({ issuerUri });
|
|
51
|
+
|
|
52
52
|
const getCandidateIssuerUri = (params: { kcHttpRelativePath: string | undefined }) => {
|
|
53
53
|
const { kcHttpRelativePath } = params;
|
|
54
54
|
|
|
55
|
-
return `${
|
|
56
|
-
kcHttpRelativePath
|
|
57
|
-
}/realms/${
|
|
55
|
+
return `${keycloakUtils.issuerUriParsed.origin}${
|
|
56
|
+
kcHttpRelativePath ?? ""
|
|
57
|
+
}/realms/${encodeURIComponent(keycloakUtils.issuerUriParsed.realm)}`;
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
if (
|
|
60
|
+
if (keycloakUtils.issuerUriParsed.kcHttpRelativePath === undefined) {
|
|
61
61
|
const issuerUri_candidate = getCandidateIssuerUri({ kcHttpRelativePath: "/auth" });
|
|
62
62
|
|
|
63
63
|
const isValid = await getIsValidRemoteJson(`${issuerUri_candidate}${WELL_KNOWN_PATH}`);
|
|
@@ -84,7 +84,7 @@ export async function createWellKnownOidcConfigurationEndpointUnreachableInitial
|
|
|
84
84
|
`Your Keycloak server is configured with KC_HTTP_RELATIVE_PATH=/`,
|
|
85
85
|
`The issuerUri you provided: ${issuerUri}`,
|
|
86
86
|
`The correct issuerUri is: ${issuerUri_candidate}`,
|
|
87
|
-
`(You should remove the ${
|
|
87
|
+
`(You should remove the ${keycloakUtils.issuerUriParsed.kcHttpRelativePath} portion.)`
|
|
88
88
|
].join("\n"),
|
|
89
89
|
isAuthServerLikelyDown: false
|
|
90
90
|
});
|
|
@@ -96,7 +96,7 @@ export async function createWellKnownOidcConfigurationEndpointUnreachableInitial
|
|
|
96
96
|
commonFallbackMessagePart,
|
|
97
97
|
``,
|
|
98
98
|
`Given the shape of the issuerUri you provided, it seems that you are using Keycloak.`,
|
|
99
|
-
`- Make sure the realm '${
|
|
99
|
+
`- Make sure the realm '${keycloakUtils.issuerUriParsed.realm}' exists.`,
|
|
100
100
|
`- Check the KC_HTTP_RELATIVE_PATH that you might have configured your keycloak server with.`,
|
|
101
101
|
` For example if you have KC_HTTP_RELATIVE_PATH=/xxx the issuerUri should be ${getCandidateIssuerUri(
|
|
102
102
|
{ kcHttpRelativePath: "/xxx" }
|
|
@@ -107,22 +107,22 @@ export async function createWellKnownOidcConfigurationEndpointUnreachableInitial
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export async function createIframeTimeoutInitializationError(params: {
|
|
110
|
-
|
|
110
|
+
redirectUri: string;
|
|
111
111
|
issuerUri: string;
|
|
112
112
|
clientId: string;
|
|
113
113
|
noIframe: boolean;
|
|
114
114
|
}): Promise<OidcInitializationError> {
|
|
115
|
-
const {
|
|
115
|
+
const { redirectUri, issuerUri, clientId, noIframe } = params;
|
|
116
116
|
|
|
117
117
|
iframe_blocked: {
|
|
118
118
|
if (noIframe) {
|
|
119
119
|
break iframe_blocked;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
const headersOrError = await fetch(
|
|
122
|
+
const headersOrError = await fetch(redirectUri).then(
|
|
123
123
|
response => {
|
|
124
124
|
if (!response.ok) {
|
|
125
|
-
return new Error(`${
|
|
125
|
+
return new Error(`${redirectUri} responded with a ${response.status} status code.`);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
return {
|
|
@@ -197,7 +197,7 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
197
197
|
return new OidcInitializationError({
|
|
198
198
|
isAuthServerLikelyDown: false,
|
|
199
199
|
messageOrCause: [
|
|
200
|
-
`${
|
|
200
|
+
`${redirectUri} is currently served by your web server with the HTTP header \`${key_problem}: ${headers[key_problem]}\`.\n`,
|
|
201
201
|
"This header prevents the silent sign-in process from working.\n",
|
|
202
202
|
"Refer to this documentation page to fix this issue: https://docs.oidc-spa.dev/v/v7/resources/iframe-related-issues"
|
|
203
203
|
].join(" ")
|
|
@@ -217,28 +217,28 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
217
217
|
`- Either the client ID "${clientId}" does not exist, or\n`,
|
|
218
218
|
`- You forgot to add the OIDC callback URL to the list of Valid Redirect URIs.\n`,
|
|
219
219
|
`Client ID: "${clientId}"\n`,
|
|
220
|
-
`Callback URL to add to the list of Valid Redirect URIs: "${
|
|
221
|
-
...(() => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (!kc) {
|
|
220
|
+
`Callback URL to add to the list of Valid Redirect URIs: "${redirectUri}"\n\n`,
|
|
221
|
+
...(await (async () => {
|
|
222
|
+
if (!isKeycloak({ issuerUri })) {
|
|
225
223
|
return [
|
|
226
224
|
"Check the documentation of your OIDC server to learn how to configure the public client (Authorization Code Flow + PKCE) properly."
|
|
227
225
|
];
|
|
228
226
|
}
|
|
229
227
|
|
|
228
|
+
const kc = (await import("../keycloak")).createKeycloakUtils({ issuerUri });
|
|
229
|
+
|
|
230
230
|
return [
|
|
231
231
|
`It seems you are using Keycloak. Follow these steps to resolve the issue:\n\n`,
|
|
232
232
|
`1. Go to the Keycloak admin console: ${kc.adminConsoleUrl_master}\n`,
|
|
233
233
|
`2. Log in as an admin user.\n`,
|
|
234
|
-
`3. In the top left corner select the realm "${kc.realm}".\n`,
|
|
234
|
+
`3. In the top left corner select the realm "${kc.issuerUriParsed.realm}".\n`,
|
|
235
235
|
`4. In the left menu, click on "Clients".\n`,
|
|
236
236
|
`5. Locate the client "${clientId}" in the list and click on it.\n`,
|
|
237
|
-
`6. Find "Valid Redirect URIs" and add "${
|
|
237
|
+
`6. Find "Valid Redirect URIs" and add "${redirectUri}" to the list.\n`,
|
|
238
238
|
`7. Save the changes.\n\n`,
|
|
239
239
|
`For more information, refer to the documentation: https://docs.oidc-spa.dev/v/v7/providers-configuration/keycloak`
|
|
240
240
|
];
|
|
241
|
-
})(),
|
|
241
|
+
})()),
|
|
242
242
|
"\n\n",
|
|
243
243
|
"If nothing works, you can try disabling the use of iframe: https://docs.oidc-spa.dev/resources/iframe-related-issues\n",
|
|
244
244
|
"with some OIDC provider it might solve the issue."
|
|
@@ -246,7 +246,7 @@ export async function createIframeTimeoutInitializationError(params: {
|
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
export function createFailedToFetchTokenEndpointInitializationError(params: {
|
|
249
|
+
export async function createFailedToFetchTokenEndpointInitializationError(params: {
|
|
250
250
|
issuerUri: string;
|
|
251
251
|
clientId: string;
|
|
252
252
|
}) {
|
|
@@ -260,27 +260,27 @@ export function createFailedToFetchTokenEndpointInitializationError(params: {
|
|
|
260
260
|
`Make sure you have added '${window.location.origin}' to the list of Web Origins`,
|
|
261
261
|
`in the '${clientId}' client configuration of your OIDC server.\n`,
|
|
262
262
|
"\n",
|
|
263
|
-
...(() => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (kc === undefined) {
|
|
263
|
+
...(await (async () => {
|
|
264
|
+
if (!isKeycloak({ issuerUri })) {
|
|
267
265
|
return [
|
|
268
266
|
"Check the documentation of your OIDC server to learn how to configure the public client (Authorization Code Flow + PKCE) properly."
|
|
269
267
|
];
|
|
270
268
|
}
|
|
271
269
|
|
|
270
|
+
const kc = (await import("../keycloak")).createKeycloakUtils({ issuerUri });
|
|
271
|
+
|
|
272
272
|
return [
|
|
273
273
|
`Since it seems that you are using Keycloak, here are the steps to follow:\n`,
|
|
274
274
|
`1. Go to the Keycloak admin console: ${kc.adminConsoleUrl_master}\n`,
|
|
275
275
|
`2. Log in as an admin user.\n`,
|
|
276
|
-
`3. In the top left corner select the realm "${kc.realm}".\n`,
|
|
276
|
+
`3. In the top left corner select the realm "${kc.issuerUriParsed.realm}".\n`,
|
|
277
277
|
`4. In the left menu, click on "Clients".\n`,
|
|
278
278
|
`5. Find '${clientId}' in the list of clients and click on it.\n`,
|
|
279
279
|
`6. Find 'Web Origins' and add '${window.location.origin}' to the list.\n`,
|
|
280
280
|
`7. Save the changes.\n\n`,
|
|
281
281
|
`More info: https://docs.oidc-spa.dev/v/v7/providers-configuration/keycloak`
|
|
282
282
|
];
|
|
283
|
-
})()
|
|
283
|
+
})())
|
|
284
284
|
].join(" ")
|
|
285
285
|
});
|
|
286
286
|
}
|
package/src/core/OidcMetadata.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type OidcMetadata as OidcClientTsOidcMetadata } from "../vendor/frontend/oidc-client-ts
|
|
1
|
+
import { type OidcMetadata as OidcClientTsOidcMetadata } from "../vendor/frontend/oidc-client-ts";
|
|
2
2
|
import { assert, type Equals } from "../vendor/frontend/tsafe";
|
|
3
3
|
|
|
4
4
|
/**
|
package/src/core/StateData.ts
CHANGED
|
@@ -36,34 +36,34 @@ export namespace StateData {
|
|
|
36
36
|
const STATE_QUERY_PARAM_VALUE_IDENTIFIER_PREFIX = "b2lkYy1zcGEu";
|
|
37
37
|
const RANDOM_STRING_LENGTH = 32 - STATE_QUERY_PARAM_VALUE_IDENTIFIER_PREFIX.length;
|
|
38
38
|
|
|
39
|
-
export function
|
|
39
|
+
export function generateStateUrlParamValue(): string {
|
|
40
40
|
return `${STATE_QUERY_PARAM_VALUE_IDENTIFIER_PREFIX}${generateUrlSafeRandom({
|
|
41
41
|
length: RANDOM_STRING_LENGTH
|
|
42
42
|
})}`;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function getIsStatQueryParamValue(params: {
|
|
46
|
-
const {
|
|
45
|
+
export function getIsStatQueryParamValue(params: { maybeStateUrlParamValue: string }): boolean {
|
|
46
|
+
const { maybeStateUrlParamValue } = params;
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
maybeStateUrlParamValue.startsWith(STATE_QUERY_PARAM_VALUE_IDENTIFIER_PREFIX) &&
|
|
50
|
+
maybeStateUrlParamValue.length ===
|
|
51
51
|
STATE_QUERY_PARAM_VALUE_IDENTIFIER_PREFIX.length + RANDOM_STRING_LENGTH
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export const STATE_STORE_KEY_PREFIX = "oidc.";
|
|
56
56
|
|
|
57
|
-
function getKey(params: {
|
|
58
|
-
const {
|
|
57
|
+
function getKey(params: { stateUrlParamValue: string }) {
|
|
58
|
+
const { stateUrlParamValue } = params;
|
|
59
59
|
|
|
60
|
-
return `${STATE_STORE_KEY_PREFIX}${
|
|
60
|
+
return `${STATE_STORE_KEY_PREFIX}${stateUrlParamValue}`;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function getStateStore(params: {
|
|
64
|
-
const {
|
|
63
|
+
function getStateStore(params: { stateUrlParamValue: string }): { data: StateData } | undefined {
|
|
64
|
+
const { stateUrlParamValue } = params;
|
|
65
65
|
|
|
66
|
-
const item = localStorage.getItem(getKey({
|
|
66
|
+
const item = localStorage.getItem(getKey({ stateUrlParamValue }));
|
|
67
67
|
|
|
68
68
|
if (item === null) {
|
|
69
69
|
return undefined;
|
|
@@ -81,21 +81,21 @@ function getStateStore(params: { stateQueryParamValue: string }): { data: StateD
|
|
|
81
81
|
return obj;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function setStateStore(params: {
|
|
85
|
-
const {
|
|
84
|
+
function setStateStore(params: { stateUrlParamValue: string; obj: { data: StateData } }) {
|
|
85
|
+
const { stateUrlParamValue, obj } = params;
|
|
86
86
|
|
|
87
|
-
localStorage.setItem(getKey({
|
|
87
|
+
localStorage.setItem(getKey({ stateUrlParamValue }), JSON.stringify(obj));
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
export function clearStateStore(params: {
|
|
91
|
-
const {
|
|
92
|
-
localStorage.removeItem(getKey({
|
|
90
|
+
export function clearStateStore(params: { stateUrlParamValue: string }) {
|
|
91
|
+
const { stateUrlParamValue } = params;
|
|
92
|
+
localStorage.removeItem(getKey({ stateUrlParamValue }));
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
export function getStateData(params: {
|
|
96
|
-
const {
|
|
95
|
+
export function getStateData(params: { stateUrlParamValue: string }): StateData | undefined {
|
|
96
|
+
const { stateUrlParamValue } = params;
|
|
97
97
|
|
|
98
|
-
const stateStore = getStateStore({
|
|
98
|
+
const stateStore = getStateStore({ stateUrlParamValue });
|
|
99
99
|
|
|
100
100
|
if (stateStore === undefined) {
|
|
101
101
|
return undefined;
|
|
@@ -104,15 +104,15 @@ export function getStateData(params: { stateQueryParamValue: string }): StateDat
|
|
|
104
104
|
return stateStore.data;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
export function markStateDataAsProcessedByCallback(params: {
|
|
108
|
-
const {
|
|
107
|
+
export function markStateDataAsProcessedByCallback(params: { stateUrlParamValue: string }) {
|
|
108
|
+
const { stateUrlParamValue } = params;
|
|
109
109
|
|
|
110
|
-
const obj = getStateStore({
|
|
110
|
+
const obj = getStateStore({ stateUrlParamValue });
|
|
111
111
|
|
|
112
112
|
assert(obj !== undefined, "180465");
|
|
113
113
|
assert(obj.data.context === "redirect", "649531");
|
|
114
114
|
|
|
115
115
|
obj.data.hasBeenProcessedByCallback = true;
|
|
116
116
|
|
|
117
|
-
setStateStore({
|
|
117
|
+
setStateStore({ stateUrlParamValue, obj });
|
|
118
118
|
}
|
package/src/core/createOidc.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
WebStorageStateStore,
|
|
4
4
|
type User as OidcClientTsUser,
|
|
5
5
|
InMemoryWebStorage
|
|
6
|
-
} from "../vendor/frontend/oidc-client-ts
|
|
6
|
+
} from "../vendor/frontend/oidc-client-ts";
|
|
7
7
|
import type { OidcMetadata } from "./OidcMetadata";
|
|
8
8
|
import { id, assert, is, type Equals } from "../vendor/frontend/tsafe";
|
|
9
9
|
import { setTimeout, clearTimeout } from "../tools/workerTimers";
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
createIframeTimeoutInitializationError,
|
|
19
19
|
createWellKnownOidcConfigurationEndpointUnreachableInitializationError
|
|
20
20
|
} from "./OidcInitializationError";
|
|
21
|
-
import { type StateData,
|
|
21
|
+
import { type StateData, generateStateUrlParamValue, STATE_STORE_KEY_PREFIX } from "./StateData";
|
|
22
22
|
import { notifyOtherTabsOfLogout, getPrOtherTabLogout } from "./logoutPropagationToOtherTabs";
|
|
23
23
|
import { notifyOtherTabsOfLogin, getPrOtherTabLogin } from "./loginPropagationToOtherTabs";
|
|
24
24
|
import { getConfigId } from "./configId";
|
|
@@ -41,10 +41,8 @@ import {
|
|
|
41
41
|
} from "./ongoingLoginOrRefreshProcesses";
|
|
42
42
|
import { initialLocationHref } from "./initialLocationHref";
|
|
43
43
|
import { createGetIsNewBrowserSession } from "./isNewBrowserSession";
|
|
44
|
-
import { trustedFetch } from "./trustedFetch";
|
|
45
44
|
import { getIsOnline } from "../tools/getIsOnline";
|
|
46
|
-
|
|
47
|
-
handleOidcCallback();
|
|
45
|
+
import { isKeycloak } from "../keycloak/isKeycloak";
|
|
48
46
|
|
|
49
47
|
// NOTE: Replaced at build time
|
|
50
48
|
const VERSION = "{{OIDC_SPA_VERSION}}";
|
|
@@ -94,6 +92,10 @@ export type ParamsOfCreateOidc<
|
|
|
94
92
|
*/
|
|
95
93
|
extraTokenParams?: Record<string, string | undefined> | (() => Record<string, string | undefined>);
|
|
96
94
|
/**
|
|
95
|
+
* Usage discouraged, it's here because we don't want to assume too much on your
|
|
96
|
+
* usecase but I can't think of a scenario where you would want anything
|
|
97
|
+
* other than the current page.
|
|
98
|
+
*
|
|
97
99
|
* Where to redirect after successful login.
|
|
98
100
|
* Default: window.location.href (here)
|
|
99
101
|
*
|
|
@@ -126,6 +128,10 @@ export type ParamsOfCreateOidc<
|
|
|
126
128
|
idleSessionLifetimeInSeconds?: number;
|
|
127
129
|
|
|
128
130
|
/**
|
|
131
|
+
* Usage discouraged, this parameter exists because we don't want to assume
|
|
132
|
+
* too much about your usecase but I can't think of a scenario where you would
|
|
133
|
+
* want anything other than the current page.
|
|
134
|
+
*
|
|
129
135
|
* Default: { redirectTo: "current page" }
|
|
130
136
|
*/
|
|
131
137
|
autoLogoutParams?: Parameters<Oidc.LoggedIn<any>["logout"]>[0];
|
|
@@ -319,18 +325,12 @@ export async function createOidc_nonMemoized<
|
|
|
319
325
|
return extraTokenParamsOrGetter;
|
|
320
326
|
})();
|
|
321
327
|
|
|
322
|
-
const
|
|
328
|
+
const homeUrlAndRedirectUri = toFullyQualifiedUrl({
|
|
323
329
|
urlish: homeUrl_params,
|
|
324
330
|
doAssertNoQueryParams: true,
|
|
325
331
|
doOutputWithTrailingSlash: true
|
|
326
332
|
});
|
|
327
333
|
|
|
328
|
-
const callbackUri = toFullyQualifiedUrl({
|
|
329
|
-
urlish: homeUrl,
|
|
330
|
-
doAssertNoQueryParams: true,
|
|
331
|
-
doOutputWithTrailingSlash: true
|
|
332
|
-
});
|
|
333
|
-
|
|
334
334
|
log?.(
|
|
335
335
|
`Calling createOidc v${VERSION} ${JSON.stringify(
|
|
336
336
|
{
|
|
@@ -338,8 +338,7 @@ export async function createOidc_nonMemoized<
|
|
|
338
338
|
clientId,
|
|
339
339
|
scopes,
|
|
340
340
|
configId,
|
|
341
|
-
|
|
342
|
-
callbackUri
|
|
341
|
+
homeUrlAndRedirectUri
|
|
343
342
|
},
|
|
344
343
|
null,
|
|
345
344
|
2
|
|
@@ -354,19 +353,13 @@ export async function createOidc_nonMemoized<
|
|
|
354
353
|
}
|
|
355
354
|
}
|
|
356
355
|
|
|
357
|
-
const
|
|
356
|
+
const stateUrlParamValue_instance = generateStateUrlParamValue();
|
|
358
357
|
|
|
359
358
|
const canUseIframe = (() => {
|
|
360
359
|
if (noIframe) {
|
|
361
360
|
return false;
|
|
362
361
|
}
|
|
363
362
|
|
|
364
|
-
// NOTE: Electron
|
|
365
|
-
if (!/https?:\/\//.test(callbackUri)) {
|
|
366
|
-
log?.("We won't use iframe, callbackUri uses a custom protocol.");
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
363
|
third_party_cookies: {
|
|
371
364
|
const isOidcServerThirdPartyRelativeToApp =
|
|
372
365
|
getHaveSharedParentDomain({
|
|
@@ -408,12 +401,13 @@ export async function createOidc_nonMemoized<
|
|
|
408
401
|
let isUserStoreInMemoryOnly: boolean;
|
|
409
402
|
|
|
410
403
|
const oidcClientTsUserManager = new OidcClientTsUserManager({
|
|
411
|
-
|
|
404
|
+
stateUrlParamValue: stateUrlParamValue_instance,
|
|
412
405
|
authority: issuerUri,
|
|
413
406
|
client_id: clientId,
|
|
414
|
-
redirect_uri:
|
|
415
|
-
silent_redirect_uri:
|
|
416
|
-
post_logout_redirect_uri:
|
|
407
|
+
redirect_uri: homeUrlAndRedirectUri,
|
|
408
|
+
silent_redirect_uri: homeUrlAndRedirectUri,
|
|
409
|
+
post_logout_redirect_uri: homeUrlAndRedirectUri,
|
|
410
|
+
response_mode: isKeycloak({ issuerUri }) ? "fragment" : "query",
|
|
417
411
|
response_type: "code",
|
|
418
412
|
scope: Array.from(new Set(["openid", ...scopes])).join(" "),
|
|
419
413
|
automaticSilentRenew: false,
|
|
@@ -445,7 +439,6 @@ export async function createOidc_nonMemoized<
|
|
|
445
439
|
}),
|
|
446
440
|
stateStore: new WebStorageStateStore({ store: localStorage, prefix: STATE_STORE_KEY_PREFIX }),
|
|
447
441
|
client_secret: __unsafe_clientSecret,
|
|
448
|
-
fetch: trustedFetch,
|
|
449
442
|
metadata: __metadata
|
|
450
443
|
});
|
|
451
444
|
|
|
@@ -457,7 +450,7 @@ export async function createOidc_nonMemoized<
|
|
|
457
450
|
transformUrlBeforeRedirect,
|
|
458
451
|
getExtraQueryParams,
|
|
459
452
|
getExtraTokenParams,
|
|
460
|
-
homeUrl,
|
|
453
|
+
homeUrl: homeUrlAndRedirectUri,
|
|
461
454
|
evtIsUserLoggedIn,
|
|
462
455
|
log
|
|
463
456
|
});
|
|
@@ -676,7 +669,7 @@ export async function createOidc_nonMemoized<
|
|
|
676
669
|
|
|
677
670
|
const result_loginSilent = await loginSilent({
|
|
678
671
|
oidcClientTsUserManager,
|
|
679
|
-
|
|
672
|
+
stateUrlParamValue_instance,
|
|
680
673
|
configId,
|
|
681
674
|
transformUrlBeforeRedirect,
|
|
682
675
|
getExtraQueryParams,
|
|
@@ -696,7 +689,7 @@ export async function createOidc_nonMemoized<
|
|
|
696
689
|
);
|
|
697
690
|
case "timeout":
|
|
698
691
|
return createIframeTimeoutInitializationError({
|
|
699
|
-
|
|
692
|
+
redirectUri: homeUrlAndRedirectUri,
|
|
700
693
|
clientId,
|
|
701
694
|
issuerUri,
|
|
702
695
|
noIframe
|
|
@@ -1022,7 +1015,7 @@ export async function createOidc_nonMemoized<
|
|
|
1022
1015
|
case "current page":
|
|
1023
1016
|
return window.location.href;
|
|
1024
1017
|
case "home":
|
|
1025
|
-
return
|
|
1018
|
+
return homeUrlAndRedirectUri;
|
|
1026
1019
|
case "specific url":
|
|
1027
1020
|
return toFullyQualifiedUrl({
|
|
1028
1021
|
urlish: params.url,
|
|
@@ -1128,7 +1121,7 @@ export async function createOidc_nonMemoized<
|
|
|
1128
1121
|
|
|
1129
1122
|
const result_loginSilent = await loginSilent({
|
|
1130
1123
|
oidcClientTsUserManager,
|
|
1131
|
-
|
|
1124
|
+
stateUrlParamValue_instance,
|
|
1132
1125
|
configId,
|
|
1133
1126
|
transformUrlBeforeRedirect,
|
|
1134
1127
|
getExtraQueryParams,
|
|
@@ -7,11 +7,8 @@ import {
|
|
|
7
7
|
import { assert, id } from "../vendor/frontend/tsafe";
|
|
8
8
|
import type { AuthResponse } from "./AuthResponse";
|
|
9
9
|
import { initialLocationHref } from "./initialLocationHref";
|
|
10
|
-
import { captureFetch } from "./trustedFetch";
|
|
11
10
|
import { encryptAuthResponse } from "./iframeMessageProtection";
|
|
12
11
|
|
|
13
|
-
captureFetch();
|
|
14
|
-
|
|
15
12
|
const globalContext = {
|
|
16
13
|
previousCall: id<{ isHandled: boolean } | undefined>(undefined)
|
|
17
14
|
};
|
|
@@ -27,30 +24,50 @@ export function handleOidcCallback(): { isHandled: boolean } {
|
|
|
27
24
|
function handleOidcCallback_nonMemoized(): { isHandled: boolean } {
|
|
28
25
|
const location_urlObj = new URL(initialLocationHref);
|
|
29
26
|
|
|
30
|
-
const
|
|
31
|
-
|
|
27
|
+
const stateUrlParamValue_wrap = (() => {
|
|
28
|
+
fragment: {
|
|
29
|
+
const stateUrlParamValue = new URLSearchParams(location_urlObj.hash.replace(/^#/, "")).get(
|
|
30
|
+
"state"
|
|
31
|
+
);
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (stateUrlParamValue === null) {
|
|
34
|
+
break fragment;
|
|
35
|
+
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
if (!getIsStatQueryParamValue({ maybeStateUrlParamValue: stateUrlParamValue })) {
|
|
38
|
+
break fragment;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { stateUrlParamValue, isFragment: true };
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
location_urlObj.searchParams.get("
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
query: {
|
|
45
|
+
const stateUrlParamValue = location_urlObj.searchParams.get("state");
|
|
46
|
+
|
|
47
|
+
if (stateUrlParamValue === null) {
|
|
48
|
+
break query;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!getIsStatQueryParamValue({ maybeStateUrlParamValue: stateUrlParamValue })) {
|
|
52
|
+
break query;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
location_urlObj.searchParams.get("client_id") !== null &&
|
|
57
|
+
location_urlObj.searchParams.get("response_type") !== null &&
|
|
58
|
+
location_urlObj.searchParams.get("redirect_uri") !== null
|
|
59
|
+
) {
|
|
60
|
+
// NOTE: We are probably in a Keycloakify theme and oidc-spa was loaded by mistake.
|
|
61
|
+
break query;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { stateUrlParamValue, isFragment: false };
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
return
|
|
67
|
+
return undefined;
|
|
51
68
|
})();
|
|
52
69
|
|
|
53
|
-
if (
|
|
70
|
+
if (stateUrlParamValue_wrap === undefined) {
|
|
54
71
|
const backForwardTracker = readBackForwardTracker();
|
|
55
72
|
|
|
56
73
|
if (backForwardTracker !== undefined) {
|
|
@@ -67,12 +84,14 @@ function handleOidcCallback_nonMemoized(): { isHandled: boolean } {
|
|
|
67
84
|
|
|
68
85
|
const isHandled = true;
|
|
69
86
|
|
|
87
|
+
const { stateUrlParamValue, isFragment } = stateUrlParamValue_wrap;
|
|
88
|
+
|
|
70
89
|
console.log = () => {};
|
|
71
90
|
console.warn = () => {};
|
|
72
91
|
console.error = () => {};
|
|
73
92
|
console.debug = () => {};
|
|
74
93
|
|
|
75
|
-
const stateData = getStateData({
|
|
94
|
+
const stateData = getStateData({ stateUrlParamValue });
|
|
76
95
|
|
|
77
96
|
if (
|
|
78
97
|
stateData === undefined ||
|
|
@@ -125,7 +144,9 @@ function handleOidcCallback_nonMemoized(): { isHandled: boolean } {
|
|
|
125
144
|
|
|
126
145
|
const authResponse: AuthResponse = { state: "" };
|
|
127
146
|
|
|
128
|
-
for (const [key, value] of
|
|
147
|
+
for (const [key, value] of isFragment
|
|
148
|
+
? new URLSearchParams(location_urlObj.hash.replace(/^#/, ""))
|
|
149
|
+
: location_urlObj.searchParams) {
|
|
129
150
|
authResponse[key] = value;
|
|
130
151
|
}
|
|
131
152
|
|
|
@@ -138,7 +159,7 @@ function handleOidcCallback_nonMemoized(): { isHandled: boolean } {
|
|
|
138
159
|
}).then(({ encryptedMessage }) => parent.postMessage(encryptedMessage, location.origin));
|
|
139
160
|
break;
|
|
140
161
|
case "redirect":
|
|
141
|
-
markStateDataAsProcessedByCallback({
|
|
162
|
+
markStateDataAsProcessedByCallback({ stateUrlParamValue });
|
|
142
163
|
clearBackForwardTracker();
|
|
143
164
|
writeRedirectAuthResponses({
|
|
144
165
|
authResponses: [...readRedirectAuthResponses(), authResponse]
|
|
@@ -232,7 +253,7 @@ export function retrieveRedirectAuthResponseAndStateData(params: {
|
|
|
232
253
|
| undefined = undefined;
|
|
233
254
|
|
|
234
255
|
for (const authResponse of [...authResponses]) {
|
|
235
|
-
const stateData = getStateData({
|
|
256
|
+
const stateData = getStateData({ stateUrlParamValue: authResponse.state });
|
|
236
257
|
|
|
237
258
|
if (stateData === undefined) {
|
|
238
259
|
// NOTE: We do not understand how this can happen but it can.
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { assert } from "tsafe
|
|
1
|
+
import { assert } from "../vendor/frontend/tsafe";
|
|
2
2
|
import { asymmetricEncrypt, asymmetricDecrypt, generateKeys } from "../tools/asymmetricEncryption";
|
|
3
3
|
import { type AuthResponse } from "./AuthResponse";
|
|
4
4
|
|
|
5
|
+
const sessionStorage_original = window.sessionStorage;
|
|
5
6
|
const setItem_real = Storage.prototype.setItem;
|
|
6
7
|
|
|
7
8
|
const SESSION_STORAGE_PREFIX = "oidc-spa_iframe_authResponse_publicKey_";
|
|
8
9
|
|
|
9
10
|
export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
|
|
10
11
|
const setItem_protected = function setItem(this: any, key: string, value: string): void {
|
|
11
|
-
if (this !==
|
|
12
|
+
if (this !== sessionStorage_original) {
|
|
12
13
|
return setItem_real.call(this, key, value);
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -18,7 +19,7 @@ export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
|
|
|
18
19
|
);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
return setItem_real.call(
|
|
22
|
+
return setItem_real.call(sessionStorage_original, key, value);
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
{
|
|
@@ -36,18 +37,18 @@ export function preventSessionStorageSetItemOfPublicKeyByThirdParty() {
|
|
|
36
37
|
|
|
37
38
|
const ENCRYPTED_AUTH_RESPONSES_PREFIX = "oidc-spa_encrypted_authResponse_";
|
|
38
39
|
|
|
39
|
-
function getSessionStorageKey(params: {
|
|
40
|
-
const {
|
|
40
|
+
function getSessionStorageKey(params: { stateUrlParamValue: string }) {
|
|
41
|
+
const { stateUrlParamValue } = params;
|
|
41
42
|
|
|
42
|
-
return `${SESSION_STORAGE_PREFIX}${
|
|
43
|
+
return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
export async function initIframeMessageProtection(params: {
|
|
46
|
-
const {
|
|
46
|
+
export async function initIframeMessageProtection(params: { stateUrlParamValue: string }) {
|
|
47
|
+
const { stateUrlParamValue } = params;
|
|
47
48
|
|
|
48
49
|
const { publicKey, privateKey } = await generateKeys();
|
|
49
50
|
|
|
50
|
-
const sessionStorageKey = getSessionStorageKey({
|
|
51
|
+
const sessionStorageKey = getSessionStorageKey({ stateUrlParamValue });
|
|
51
52
|
|
|
52
53
|
setItem_real.call(sessionStorage, sessionStorageKey, publicKey);
|
|
53
54
|
|
|
@@ -83,7 +84,7 @@ export async function encryptAuthResponse(params: { authResponse: AuthResponse }
|
|
|
83
84
|
const { authResponse } = params;
|
|
84
85
|
|
|
85
86
|
const publicKey = sessionStorage.getItem(
|
|
86
|
-
getSessionStorageKey({
|
|
87
|
+
getSessionStorageKey({ stateUrlParamValue: authResponse.state })
|
|
87
88
|
);
|
|
88
89
|
|
|
89
90
|
assert(publicKey !== null, "2293302");
|
package/src/core/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { Oidc } from "./Oidc";
|
|
2
2
|
export { createOidc, type ParamsOfCreateOidc } from "./createOidc";
|
|
3
3
|
export { OidcInitializationError } from "./OidcInitializationError";
|
|
4
|
-
export {
|
|
4
|
+
export { handleOidcCallback } from "./handleOidcCallback";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UserManager as OidcClientTsUserManager } from "../vendor/frontend/oidc-client-ts
|
|
1
|
+
import type { UserManager as OidcClientTsUserManager } from "../vendor/frontend/oidc-client-ts";
|
|
2
2
|
import { toFullyQualifiedUrl } from "../tools/toFullyQualifiedUrl";
|
|
3
3
|
import { assert, type Equals, noUndefined } from "../vendor/frontend/tsafe";
|
|
4
4
|
import type { StateData } from "./StateData";
|