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.
Files changed (172) hide show
  1. package/backend.js +235 -352
  2. package/backend.js.map +1 -1
  3. package/core/AuthResponse.js +12 -49
  4. package/core/AuthResponse.js.map +1 -1
  5. package/core/Oidc.d.ts +1 -2
  6. package/core/OidcInitializationError.d.ts +2 -2
  7. package/core/OidcInitializationError.js +230 -297
  8. package/core/OidcInitializationError.js.map +1 -1
  9. package/core/OidcMetadata.js +1 -1
  10. package/core/OidcMetadata.js.map +1 -1
  11. package/core/StateData.d.ts +5 -5
  12. package/core/StateData.js +25 -25
  13. package/core/StateData.js.map +1 -1
  14. package/core/configId.js +1 -1
  15. package/core/configId.js.map +1 -1
  16. package/core/createOidc.d.ts +8 -0
  17. package/core/createOidc.js +999 -1294
  18. package/core/createOidc.js.map +1 -1
  19. package/core/evtIsUserActive.js +26 -27
  20. package/core/evtIsUserActive.js.map +1 -1
  21. package/core/handleOidcCallback.js +99 -154
  22. package/core/handleOidcCallback.js.map +1 -1
  23. package/core/iframeMessageProtection.d.ts +1 -1
  24. package/core/iframeMessageProtection.js +43 -108
  25. package/core/iframeMessageProtection.js.map +1 -1
  26. package/core/index.d.ts +1 -1
  27. package/core/index.js +3 -3
  28. package/core/index.js.map +1 -1
  29. package/core/initialLocationHref.js +1 -1
  30. package/core/initialLocationHref.js.map +1 -1
  31. package/core/isNewBrowserSession.js +8 -8
  32. package/core/isNewBrowserSession.js.map +1 -1
  33. package/core/loginOrGoToAuthServer.d.ts +1 -1
  34. package/core/loginOrGoToAuthServer.js +188 -310
  35. package/core/loginOrGoToAuthServer.js.map +1 -1
  36. package/core/loginPropagationToOtherTabs.js +15 -16
  37. package/core/loginPropagationToOtherTabs.js.map +1 -1
  38. package/core/loginSilent.d.ts +2 -3
  39. package/core/loginSilent.js +118 -214
  40. package/core/loginSilent.js.map +1 -1
  41. package/core/logoutPropagationToOtherTabs.js +15 -16
  42. package/core/logoutPropagationToOtherTabs.js.map +1 -1
  43. package/core/oidcClientTsUserToTokens.d.ts +1 -1
  44. package/core/oidcClientTsUserToTokens.js +75 -72
  45. package/core/oidcClientTsUserToTokens.js.map +1 -1
  46. package/core/ongoingLoginOrRefreshProcesses.js +23 -89
  47. package/core/ongoingLoginOrRefreshProcesses.js.map +1 -1
  48. package/core/persistedAuthState.js +13 -13
  49. package/core/persistedAuthState.js.map +1 -1
  50. package/entrypoint.js +9 -9
  51. package/entrypoint.js.map +1 -1
  52. package/index.d.ts +1 -1
  53. package/index.js +1 -2
  54. package/index.js.map +1 -1
  55. package/keycloak/index.d.ts +3 -0
  56. package/keycloak/index.js +8 -0
  57. package/keycloak/index.js.map +1 -0
  58. package/keycloak/isKeycloak.d.ts +3 -0
  59. package/keycloak/isKeycloak.js +20 -0
  60. package/keycloak/isKeycloak.js.map +1 -0
  61. package/keycloak/keycloak-js/Keycloak.d.ts +284 -0
  62. package/keycloak/keycloak-js/Keycloak.js +778 -0
  63. package/keycloak/keycloak-js/Keycloak.js.map +1 -0
  64. package/keycloak/keycloak-js/index.d.ts +2 -0
  65. package/keycloak/keycloak-js/index.js +6 -0
  66. package/keycloak/keycloak-js/index.js.map +1 -0
  67. package/keycloak/keycloak-js/types.d.ts +361 -0
  68. package/keycloak/keycloak-js/types.js +3 -0
  69. package/keycloak/keycloak-js/types.js.map +1 -0
  70. package/keycloak/keycloakIssuerUriParsed.d.ts +9 -0
  71. package/keycloak/keycloakIssuerUriParsed.js +19 -0
  72. package/keycloak/keycloakIssuerUriParsed.js.map +1 -0
  73. package/keycloak/keycloakUtils.d.ts +37 -0
  74. package/keycloak/keycloakUtils.js +47 -0
  75. package/keycloak/keycloakUtils.js.map +1 -0
  76. package/keycloak-js.d.ts +1 -0
  77. package/keycloak-js.js +18 -0
  78. package/keycloak-js.js.map +1 -0
  79. package/mock/oidc.js +147 -194
  80. package/mock/oidc.js.map +1 -1
  81. package/mock/react.js +2 -2
  82. package/mock/react.js.map +1 -1
  83. package/package.json +38 -9
  84. package/react/react.js +133 -244
  85. package/react/react.js.map +1 -1
  86. package/src/core/AuthResponse.ts +2 -0
  87. package/src/core/Oidc.ts +1 -2
  88. package/src/core/OidcInitializationError.ts +30 -30
  89. package/src/core/OidcMetadata.ts +1 -1
  90. package/src/core/StateData.ts +24 -24
  91. package/src/core/createOidc.ts +24 -31
  92. package/src/core/handleOidcCallback.ts +44 -23
  93. package/src/core/iframeMessageProtection.ts +11 -10
  94. package/src/core/index.ts +1 -1
  95. package/src/core/loginOrGoToAuthServer.ts +1 -1
  96. package/src/core/loginSilent.ts +14 -11
  97. package/src/core/oidcClientTsUserToTokens.ts +1 -1
  98. package/src/index.ts +1 -7
  99. package/src/keycloak/index.ts +8 -0
  100. package/src/keycloak/isKeycloak.ts +23 -0
  101. package/src/keycloak/keycloak-js/Keycloak.ts +1097 -0
  102. package/src/keycloak/keycloak-js/index.ts +2 -0
  103. package/src/keycloak/keycloak-js/types.ts +442 -0
  104. package/src/keycloak/keycloakIssuerUriParsed.ts +29 -0
  105. package/src/keycloak/keycloakUtils.ts +90 -0
  106. package/src/keycloak-js.ts +1 -0
  107. package/src/react/react.tsx +17 -1
  108. package/src/tools/decodeJwt.ts +95 -2
  109. package/src/tools/parseKeycloakIssuerUri.ts +11 -30
  110. package/src/vendor/frontend/oidc-client-ts.ts +1 -0
  111. package/src/vendor/frontend/tsafe.ts +1 -0
  112. package/tools/Deferred.js +13 -35
  113. package/tools/Deferred.js.map +1 -1
  114. package/tools/EphemeralSessionStorage.js +46 -48
  115. package/tools/EphemeralSessionStorage.js.map +1 -1
  116. package/tools/Evt.js +14 -14
  117. package/tools/Evt.js.map +1 -1
  118. package/tools/StatefulEvt.js +5 -5
  119. package/tools/StatefulEvt.js.map +1 -1
  120. package/tools/asymmetricEncryption.js +81 -172
  121. package/tools/asymmetricEncryption.js.map +1 -1
  122. package/tools/base64.js +2 -2
  123. package/tools/base64.js.map +1 -1
  124. package/tools/createObjectThatThrowsIfAccessed.js +13 -61
  125. package/tools/createObjectThatThrowsIfAccessed.js.map +1 -1
  126. package/tools/decodeJwt.d.ts +25 -2
  127. package/tools/decodeJwt.js +61 -3
  128. package/tools/decodeJwt.js.map +1 -1
  129. package/tools/generateUrlSafeRandom.js +5 -30
  130. package/tools/generateUrlSafeRandom.js.map +1 -1
  131. package/tools/getDownlinkAndRtt.js +8 -30
  132. package/tools/getDownlinkAndRtt.js.map +1 -1
  133. package/tools/getIsOnline.js +3 -3
  134. package/tools/getIsOnline.js.map +1 -1
  135. package/tools/getIsValidRemoteJson.js +12 -59
  136. package/tools/getIsValidRemoteJson.js.map +1 -1
  137. package/tools/getPrUserInteraction.js +4 -4
  138. package/tools/getPrUserInteraction.js.map +1 -1
  139. package/tools/getUserEnvironmentInfo.js +17 -12
  140. package/tools/getUserEnvironmentInfo.js.map +1 -1
  141. package/tools/haveSharedParentDomain.js +5 -5
  142. package/tools/haveSharedParentDomain.js.map +1 -1
  143. package/tools/isDev.js +2 -2
  144. package/tools/isDev.js.map +1 -1
  145. package/tools/parseKeycloakIssuerUri.d.ts +2 -0
  146. package/tools/parseKeycloakIssuerUri.js +11 -42
  147. package/tools/parseKeycloakIssuerUri.js.map +1 -1
  148. package/tools/readExpirationTimeInJwt.js +4 -4
  149. package/tools/readExpirationTimeInJwt.js.map +1 -1
  150. package/tools/startCountdown.js +17 -65
  151. package/tools/startCountdown.js.map +1 -1
  152. package/tools/subscribeToUserInteraction.js +17 -66
  153. package/tools/subscribeToUserInteraction.js.map +1 -1
  154. package/tools/toFullyQualifiedUrl.js +7 -7
  155. package/tools/toFullyQualifiedUrl.js.map +1 -1
  156. package/tools/toHumanReadableDuration.js +13 -13
  157. package/tools/toHumanReadableDuration.js.map +1 -1
  158. package/tools/urlSearchParams.js +28 -50
  159. package/tools/urlSearchParams.js.map +1 -1
  160. package/tools/workerTimers.js +10 -10
  161. package/tools/workerTimers.js.map +1 -1
  162. package/vendor/frontend/oidc-client-ts.d.ts +1 -0
  163. package/vendor/frontend/oidc-client-ts.js +3686 -0
  164. package/vendor/frontend/tsafe.d.ts +1 -0
  165. package/vendor/frontend/tsafe.js +1 -1
  166. package/core/trustedFetch.d.ts +0 -2
  167. package/core/trustedFetch.js +0 -12
  168. package/core/trustedFetch.js.map +0 -1
  169. package/src/core/trustedFetch.ts +0 -9
  170. package/src/vendor/frontend/oidc-client-ts-and-jwt-decode.ts +0 -4
  171. package/vendor/frontend/oidc-client-ts-and-jwt-decode.d.ts +0 -3
  172. 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 { parseKeycloakIssuerUri } from "../tools/parseKeycloakIssuerUri";
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 (issuerUri_parsed === undefined) {
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 `${issuerUri_parsed.origin}${
56
- kcHttpRelativePath === undefined ? "" : kcHttpRelativePath
57
- }/realms/${issuerUri_parsed.realm}`;
55
+ return `${keycloakUtils.issuerUriParsed.origin}${
56
+ kcHttpRelativePath ?? ""
57
+ }/realms/${encodeURIComponent(keycloakUtils.issuerUriParsed.realm)}`;
58
58
  };
59
59
 
60
- if (issuerUri_parsed.kcHttpRelativePath === undefined) {
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 ${issuerUri_parsed.kcHttpRelativePath} portion.)`
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 '${issuerUri_parsed.realm}' exists.`,
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
- callbackUri: string;
110
+ redirectUri: string;
111
111
  issuerUri: string;
112
112
  clientId: string;
113
113
  noIframe: boolean;
114
114
  }): Promise<OidcInitializationError> {
115
- const { callbackUri, issuerUri, clientId, noIframe } = params;
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(callbackUri).then(
122
+ const headersOrError = await fetch(redirectUri).then(
123
123
  response => {
124
124
  if (!response.ok) {
125
- return new Error(`${callbackUri} responded with a ${response.status} status code.`);
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
- `${callbackUri} is currently served by your web server with the HTTP header \`${key_problem}: ${headers[key_problem]}\`.\n`,
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: "${callbackUri}"\n\n`,
221
- ...(() => {
222
- const kc = parseKeycloakIssuerUri(issuerUri);
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 "${callbackUri}" to the list.\n`,
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
- const kc = parseKeycloakIssuerUri(issuerUri);
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
  }
@@ -1,4 +1,4 @@
1
- import { type OidcMetadata as OidcClientTsOidcMetadata } from "../vendor/frontend/oidc-client-ts-and-jwt-decode";
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
  /**
@@ -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 generateStateQueryParamValue(): string {
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: { maybeStateQueryParamValue: string }): boolean {
46
- const { maybeStateQueryParamValue } = params;
45
+ export function getIsStatQueryParamValue(params: { maybeStateUrlParamValue: string }): boolean {
46
+ const { maybeStateUrlParamValue } = params;
47
47
 
48
48
  return (
49
- maybeStateQueryParamValue.startsWith(STATE_QUERY_PARAM_VALUE_IDENTIFIER_PREFIX) &&
50
- maybeStateQueryParamValue.length ===
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: { stateQueryParamValue: string }) {
58
- const { stateQueryParamValue } = params;
57
+ function getKey(params: { stateUrlParamValue: string }) {
58
+ const { stateUrlParamValue } = params;
59
59
 
60
- return `${STATE_STORE_KEY_PREFIX}${stateQueryParamValue}`;
60
+ return `${STATE_STORE_KEY_PREFIX}${stateUrlParamValue}`;
61
61
  }
62
62
 
63
- function getStateStore(params: { stateQueryParamValue: string }): { data: StateData } | undefined {
64
- const { stateQueryParamValue } = params;
63
+ function getStateStore(params: { stateUrlParamValue: string }): { data: StateData } | undefined {
64
+ const { stateUrlParamValue } = params;
65
65
 
66
- const item = localStorage.getItem(getKey({ stateQueryParamValue }));
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: { stateQueryParamValue: string; obj: { data: StateData } }) {
85
- const { stateQueryParamValue, obj } = params;
84
+ function setStateStore(params: { stateUrlParamValue: string; obj: { data: StateData } }) {
85
+ const { stateUrlParamValue, obj } = params;
86
86
 
87
- localStorage.setItem(getKey({ stateQueryParamValue }), JSON.stringify(obj));
87
+ localStorage.setItem(getKey({ stateUrlParamValue }), JSON.stringify(obj));
88
88
  }
89
89
 
90
- export function clearStateStore(params: { stateQueryParamValue: string }) {
91
- const { stateQueryParamValue } = params;
92
- localStorage.removeItem(getKey({ stateQueryParamValue }));
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: { stateQueryParamValue: string }): StateData | undefined {
96
- const { stateQueryParamValue } = params;
95
+ export function getStateData(params: { stateUrlParamValue: string }): StateData | undefined {
96
+ const { stateUrlParamValue } = params;
97
97
 
98
- const stateStore = getStateStore({ stateQueryParamValue });
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: { stateQueryParamValue: string }) {
108
- const { stateQueryParamValue } = params;
107
+ export function markStateDataAsProcessedByCallback(params: { stateUrlParamValue: string }) {
108
+ const { stateUrlParamValue } = params;
109
109
 
110
- const obj = getStateStore({ stateQueryParamValue });
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({ stateQueryParamValue, obj });
117
+ setStateStore({ stateUrlParamValue, obj });
118
118
  }
@@ -3,7 +3,7 @@ import {
3
3
  WebStorageStateStore,
4
4
  type User as OidcClientTsUser,
5
5
  InMemoryWebStorage
6
- } from "../vendor/frontend/oidc-client-ts-and-jwt-decode";
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, generateStateQueryParamValue, STATE_STORE_KEY_PREFIX } from "./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 homeUrl = toFullyQualifiedUrl({
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
- homeUrl,
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 stateQueryParamValue_instance = generateStateQueryParamValue();
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
- stateQueryParamValue: stateQueryParamValue_instance,
404
+ stateUrlParamValue: stateUrlParamValue_instance,
412
405
  authority: issuerUri,
413
406
  client_id: clientId,
414
- redirect_uri: callbackUri,
415
- silent_redirect_uri: callbackUri,
416
- post_logout_redirect_uri: callbackUri,
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
- stateQueryParamValue_instance,
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
- callbackUri,
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 homeUrl;
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
- stateQueryParamValue_instance,
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 stateQueryParamValue = (() => {
31
- const stateQueryParamValue = location_urlObj.searchParams.get("state");
27
+ const stateUrlParamValue_wrap = (() => {
28
+ fragment: {
29
+ const stateUrlParamValue = new URLSearchParams(location_urlObj.hash.replace(/^#/, "")).get(
30
+ "state"
31
+ );
32
32
 
33
- if (stateQueryParamValue === null) {
34
- return undefined;
35
- }
33
+ if (stateUrlParamValue === null) {
34
+ break fragment;
35
+ }
36
36
 
37
- if (!getIsStatQueryParamValue({ maybeStateQueryParamValue: stateQueryParamValue })) {
38
- return undefined;
37
+ if (!getIsStatQueryParamValue({ maybeStateUrlParamValue: stateUrlParamValue })) {
38
+ break fragment;
39
+ }
40
+
41
+ return { stateUrlParamValue, isFragment: true };
39
42
  }
40
43
 
41
- if (
42
- location_urlObj.searchParams.get("client_id") !== null &&
43
- location_urlObj.searchParams.get("response_type") !== null &&
44
- location_urlObj.searchParams.get("redirect_uri") !== null
45
- ) {
46
- // NOTE: We are probably in a Keycloakify theme and oidc-spa was loaded by mistake.
47
- return undefined;
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 stateQueryParamValue;
67
+ return undefined;
51
68
  })();
52
69
 
53
- if (stateQueryParamValue === undefined) {
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({ stateQueryParamValue });
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 location_urlObj.searchParams) {
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({ stateQueryParamValue });
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({ stateQueryParamValue: authResponse.state });
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/assert";
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 !== sessionStorage) {
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(sessionStorage, key, value);
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: { stateQueryParamValue: string }) {
40
- const { stateQueryParamValue } = params;
40
+ function getSessionStorageKey(params: { stateUrlParamValue: string }) {
41
+ const { stateUrlParamValue } = params;
41
42
 
42
- return `${SESSION_STORAGE_PREFIX}${stateQueryParamValue}`;
43
+ return `${SESSION_STORAGE_PREFIX}${stateUrlParamValue}`;
43
44
  }
44
45
 
45
- export async function initIframeMessageProtection(params: { stateQueryParamValue: string }) {
46
- const { stateQueryParamValue } = params;
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({ stateQueryParamValue });
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({ stateQueryParamValue: authResponse.state })
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 { trustedFetch } from "./trustedFetch";
4
+ export { handleOidcCallback } from "./handleOidcCallback";
@@ -1,4 +1,4 @@
1
- import type { UserManager as OidcClientTsUserManager } from "../vendor/frontend/oidc-client-ts-and-jwt-decode";
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";