@enterprisestandard/react 0.0.3-beta.20251029.3 → 0.0.3-beta.20251110.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/dist/index.js CHANGED
@@ -31,6 +31,7 @@ function getES(es) {
31
31
 
32
32
  // src/sso.ts
33
33
  var jwksCache = new Map;
34
+ var _logoutTokenJtis = new Set;
34
35
  function sso(config) {
35
36
  const configWithDefaults = {
36
37
  ...config,
@@ -93,6 +94,52 @@ function sso(config) {
93
94
  }
94
95
  });
95
96
  }
97
+ async function logout(request, _config) {
98
+ try {
99
+ const refreshToken2 = getCookie("refresh", request);
100
+ if (refreshToken2) {
101
+ await revokeToken(refreshToken2);
102
+ }
103
+ } catch (error) {
104
+ console.warn("Failed to revoke token:", error);
105
+ }
106
+ const clearHeaders = [
107
+ ["Set-Cookie", clearCookie("access")],
108
+ ["Set-Cookie", clearCookie("id")],
109
+ ["Set-Cookie", clearCookie("refresh")],
110
+ ["Set-Cookie", clearCookie("control")],
111
+ ["Set-Cookie", clearCookie("state")]
112
+ ];
113
+ const url = new URL(request.url);
114
+ const redirectTo = url.searchParams.get("redirect");
115
+ if (redirectTo) {
116
+ return new Response("Logged out", {
117
+ status: 302,
118
+ headers: [["Location", redirectTo], ...clearHeaders]
119
+ });
120
+ }
121
+ const accept = request.headers.get("accept");
122
+ const isAjax = accept?.includes("application/json") || accept?.includes("text/javascript");
123
+ if (isAjax) {
124
+ return new Response(JSON.stringify({ success: true, message: "Logged out" }), {
125
+ status: 200,
126
+ headers: [["Content-Type", "application/json"], ...clearHeaders]
127
+ });
128
+ } else {
129
+ return new Response(`
130
+ <!DOCTYPE html><html lang="en"><body>
131
+ <h1>Logout Complete</h1>
132
+ <div style="display: none">
133
+ It is not recommended to show the default logout page. Include '?redirect=/someHomePage' or logout asynchronously.
134
+ Check the <a href="https://EnterpriseStandard.com/sso#logout">Enterprise Standard Packages</a> for more information.
135
+ </div>
136
+ </body></html>
137
+ `, {
138
+ status: 200,
139
+ headers: [["Content-Type", "text/html"], ...clearHeaders]
140
+ });
141
+ }
142
+ }
96
143
  async function callbackHandler(request) {
97
144
  if (!configWithDefaults) {
98
145
  console.error("SSO Manager not initialized");
@@ -148,6 +195,7 @@ function sso(config) {
148
195
  const expiresIn = Number(token.refresh_expires_in ?? token.expires_in ?? 3600);
149
196
  const expires = token.expires ? new Date(token.expires) : new Date(Date.now() + expiresIn * 1000);
150
197
  return {
198
+ id: idToken.sub,
151
199
  userName: idToken.preferred_username || "",
152
200
  name: idToken.name || "",
153
201
  email: idToken.email || "",
@@ -230,6 +278,31 @@ function sso(config) {
230
278
  return data;
231
279
  });
232
280
  }
281
+ async function revokeToken(token) {
282
+ try {
283
+ if (!configWithDefaults)
284
+ throw new Error("SSO Manager not initialized");
285
+ const revokeUrl = `${configWithDefaults.authority}/protocol/openid-connect/revoke`;
286
+ const body = new URLSearchParams;
287
+ body.append("token", token);
288
+ body.append("token_type_hint", "refresh_token");
289
+ body.append("client_id", configWithDefaults.client_id);
290
+ const response = await fetch(revokeUrl, {
291
+ method: "POST",
292
+ headers: {
293
+ "Content-Type": "application/x-www-form-urlencoded"
294
+ },
295
+ body: body.toString()
296
+ });
297
+ if (!response.ok) {
298
+ console.warn("Token revocation failed:", response.status, response.statusText);
299
+ } else {
300
+ console.log("Token revoked successfully");
301
+ }
302
+ } catch (error) {
303
+ console.warn("Error revoking token:", error);
304
+ }
305
+ }
233
306
  async function fetchJwks() {
234
307
  const url = configWithDefaults.jwks_uri || `${configWithDefaults.authority}/protocol/openid-connect/certs`;
235
308
  const cached = jwksCache.get(url);
@@ -391,13 +464,20 @@ function sso(config) {
391
464
  return JSON.parse(str);
392
465
  }
393
466
  async function handler(request, handlerConfig) {
394
- let { loginUrl, userUrl, errorUrl, landingUrl, tokenUrl, refreshUrl } = handlerConfig ?? {};
395
- if (!loginUrl)
396
- loginUrl = "*";
467
+ const { loginUrl, userUrl, errorUrl, landingUrl, tokenUrl, refreshUrl, logoutUrl, jwksUrl } = handlerConfig ?? {};
468
+ if (!loginUrl) {
469
+ console.error("loginUrl is required");
470
+ }
397
471
  const path = new URL(request.url).pathname;
398
472
  if (new URL(config.redirect_uri).pathname === path) {
399
473
  return callbackHandler(request);
400
474
  }
475
+ if (loginUrl === path) {
476
+ return initiateLogin({
477
+ landingUrl: landingUrl || "/",
478
+ errorUrl
479
+ });
480
+ }
401
481
  if (userUrl === path) {
402
482
  const { tokens, refreshHeaders } = await getTokenFromCookies(request);
403
483
  if (!tokens) {
@@ -433,10 +513,13 @@ function sso(config) {
433
513
  headers: refreshHeaders
434
514
  });
435
515
  }
436
- if (loginUrl === "*" || loginUrl === path) {
437
- return initiateLogin({
438
- landingUrl: landingUrl || "/",
439
- errorUrl
516
+ if (logoutUrl === path) {
517
+ return logout(request, { landingUrl: landingUrl || "/" });
518
+ }
519
+ if (jwksUrl === path) {
520
+ const jwks = await fetchJwks();
521
+ return new Response(JSON.stringify(jwks), {
522
+ headers: [["Content-Type", "application/json"]]
440
523
  });
441
524
  }
442
525
  return new Response("Not Found", { status: 404 });
@@ -446,6 +529,7 @@ function sso(config) {
446
529
  getRequiredUser,
447
530
  getJwt,
448
531
  initiateLogin,
532
+ logout,
449
533
  callbackHandler,
450
534
  handler
451
535
  };
@@ -774,9 +858,14 @@ function SSOProvider({
774
858
  }
775
859
  }
776
860
  };
861
+ const handleLogout = () => {
862
+ setUserState(null);
863
+ };
777
864
  window.addEventListener("storage", handleStorageChange);
865
+ window.addEventListener("es-sso-logout", handleLogout);
778
866
  return () => {
779
867
  window.removeEventListener("storage", handleStorageChange);
868
+ window.removeEventListener("es-sso-logout", handleLogout);
780
869
  };
781
870
  }, [disableListener, storage, actualStorageKey, isValidUser]);
782
871
  const contextValue = {
@@ -875,6 +964,41 @@ function useToken() {
875
964
  refresh
876
965
  };
877
966
  }
967
+ async function logout(logoutUrl) {
968
+ try {
969
+ const response = await fetch(logoutUrl, {
970
+ headers: { Accept: "application/json" }
971
+ });
972
+ if (!response.ok) {
973
+ return { success: false, error: `HTTP ${response.status}` };
974
+ }
975
+ const data = await response.json();
976
+ if (!data.success) {
977
+ return { success: false, error: data.message || "Logout failed" };
978
+ }
979
+ if (typeof window !== "undefined") {
980
+ for (let i = localStorage.length - 1;i >= 0; i--) {
981
+ const key = localStorage.key(i);
982
+ if (key?.startsWith("es-sso-user")) {
983
+ localStorage.removeItem(key);
984
+ }
985
+ }
986
+ for (let i = sessionStorage.length - 1;i >= 0; i--) {
987
+ const key = sessionStorage.key(i);
988
+ if (key?.startsWith("es-sso-user")) {
989
+ sessionStorage.removeItem(key);
990
+ }
991
+ }
992
+ window.dispatchEvent(new CustomEvent("es-sso-logout"));
993
+ }
994
+ return { success: true };
995
+ } catch (error) {
996
+ return {
997
+ success: false,
998
+ error: error instanceof Error ? error.message : "Network error"
999
+ };
1000
+ }
1001
+ }
878
1002
 
879
1003
  // src/index.ts
880
1004
  async function enterpriseStandard(appId, appKey, initConfig) {
@@ -920,6 +1044,7 @@ export {
920
1044
  useUser,
921
1045
  useToken,
922
1046
  oidcCallbackSchema,
1047
+ logout,
923
1048
  initiateLogin,
924
1049
  handler,
925
1050
  getUser,
package/dist/sso.d.ts CHANGED
@@ -7,13 +7,13 @@ export type SSOConfig = {
7
7
  redirect_uri: string;
8
8
  response_type: string;
9
9
  scope: string;
10
- post_logout_redirect_uri?: string;
11
10
  silent_redirect_uri?: string;
12
11
  jwks_uri?: string;
13
12
  cookies_prefix?: string;
14
13
  cookies_path?: string;
15
14
  cookies_secure?: boolean;
16
15
  cookies_same_site?: 'Strict' | 'Lax';
16
+ end_session_endpoint?: string;
17
17
  };
18
18
  export type ESConfig = {
19
19
  es?: EnterpriseStandard;
@@ -29,12 +29,16 @@ export type SSOHandlerConfig = {
29
29
  landingUrl?: string;
30
30
  tokenUrl?: string;
31
31
  refreshUrl?: string;
32
+ jwksUrl?: string;
33
+ logoutUrl?: string;
34
+ logoutCallbackUrl?: string;
32
35
  } & ESConfig;
33
36
  export type SSO = {
34
37
  getUser: (request: Request) => Promise<EnterpriseUser | undefined>;
35
38
  getRequiredUser: (request: Request) => Promise<EnterpriseUser>;
36
39
  getJwt: (request: Request) => Promise<string | undefined>;
37
40
  initiateLogin: (config: LoginConfig) => Promise<Response>;
41
+ logout: (request: Request, config?: LoginConfig) => Promise<Response>;
38
42
  callbackHandler: (request: Request) => Promise<Response>;
39
43
  handler: (request: Request, handlerConfig?: SSOHandlerConfig) => Promise<Response>;
40
44
  };
@@ -27,4 +27,8 @@ interface UseTokenReturn {
27
27
  refresh: () => Promise<void>;
28
28
  }
29
29
  export declare function useToken(): UseTokenReturn;
30
+ export declare function logout(logoutUrl: string): Promise<{
31
+ success: boolean;
32
+ error?: string;
33
+ }>;
30
34
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enterprisestandard/react",
3
- "version": "0.0.3-beta.20251029.3",
3
+ "version": "0.0.3-beta.20251110.1",
4
4
  "description": "Enterprise Standard React Components",
5
5
  "private": false,
6
6
  "main": "dist/index.js",