@explita/cloud-auth-client 0.0.1 → 0.1.0

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 (48) hide show
  1. package/README.md +25 -2
  2. package/dist/components/change-password.js +13 -11
  3. package/dist/components/icons/lock.d.ts +4 -0
  4. package/dist/components/icons/lock.js +15 -0
  5. package/dist/components/icons/logout.d.ts +4 -0
  6. package/dist/components/icons/logout.js +11 -0
  7. package/dist/components/icons/setting.d.ts +4 -0
  8. package/dist/components/icons/setting.js +12 -0
  9. package/dist/components/login-form.js +3 -4
  10. package/dist/components/optional-otp.js +2 -2
  11. package/dist/components/reset-password.js +15 -16
  12. package/dist/components/settings.js +4 -1
  13. package/dist/components/signup-form.d.ts +3 -3
  14. package/dist/components/signup-form.js +20 -21
  15. package/dist/components/toggle-2fa.js +3 -3
  16. package/dist/components/toggle-account-status.js +18 -11
  17. package/dist/components/ui/dialog.js +1 -1
  18. package/dist/components/user-card.js +22 -9
  19. package/dist/contexts/auth-provider.js +23 -18
  20. package/dist/hooks/use-token-refresher.js +11 -11
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3 -1
  23. package/dist/lib/api-client.js +39 -24
  24. package/dist/lib/api-server.js +5 -4
  25. package/dist/lib/constants.d.ts +10 -0
  26. package/dist/lib/constants.js +13 -0
  27. package/dist/lib/error.js +1 -1
  28. package/dist/lib/refresh-helper.d.ts +0 -6
  29. package/dist/lib/refresh-helper.js +12 -22
  30. package/dist/lib/utils.d.ts +3 -1
  31. package/dist/lib/utils.js +19 -14
  32. package/dist/server/index.d.ts +3 -2
  33. package/dist/server/index.js +5 -2
  34. package/dist/server/{cookie.js → next-cookie-override.js} +3 -3
  35. package/dist/server/reset-password.js +7 -0
  36. package/dist/server/role.d.ts +2 -4
  37. package/dist/server/role.js +13 -1
  38. package/dist/server/server-session.js +5 -0
  39. package/dist/server/server-token.js +5 -5
  40. package/dist/server/toggle-2fa.js +4 -0
  41. package/dist/server/{users-accounts.d.ts → user.d.ts} +3 -5
  42. package/dist/server/{users-accounts.js → user.js} +16 -4
  43. package/dist/styles.css +7 -1
  44. package/dist/types.d.ts +21 -87
  45. package/package.json +9 -9
  46. /package/dist/components/{x-icon.d.ts → icons/x-icon.d.ts} +0 -0
  47. /package/dist/components/{x-icon.js → icons/x-icon.js} +0 -0
  48. /package/dist/server/{cookie.d.ts → next-cookie-override.d.ts} +0 -0
@@ -42,7 +42,7 @@ const loader_1 = require("../components/loader");
42
42
  const error_1 = require("../lib/error");
43
43
  const use_token_refresher_1 = require("../hooks/use-token-refresher");
44
44
  const optional_otp_wrapper_1 = require("../components/optional-otp-wrapper");
45
- const refresh_helper_1 = require("../lib/refresh-helper");
45
+ const constants_1 = require("../lib/constants");
46
46
  const AuthContext = (0, react_1.createContext)(undefined);
47
47
  function AuthProvider({ children, config }) {
48
48
  const [twoFAData, setTwoFAData] = (0, react_1.useState)(null);
@@ -53,12 +53,11 @@ function AuthProvider({ children, config }) {
53
53
  const [transition, startTransition] = (0, react_1.useTransition)();
54
54
  const withFullObject = (0, utils_1.shouldPassFullUserObject)();
55
55
  const computedRouteContext = (0, utils_1.buildRouteContext)(config);
56
- const { loginUrl, resetPasswordUrl, dashboardUrl, returnPath, isExcluded } = computedRouteContext;
56
+ const { loginUrl, resetPasswordUrl, dashboardUrl, returnPathRaw, isExcluded, } = computedRouteContext;
57
57
  const { disableLoading = false } = config || {};
58
58
  async function redirectAfterLogin(authToken, tempRefreshToken) {
59
- var _a;
60
- await ((_a = config === null || config === void 0 ? void 0 : config.cookieOverride) === null || _a === void 0 ? void 0 : _a.call(config, tempRefreshToken));
61
- localStorage.setItem(refresh_helper_1.AUTH_TOKEN_KEY, authToken);
59
+ await config?.cookieOverride?.(tempRefreshToken);
60
+ localStorage.setItem(constants_1.AUTH_TOKEN_KEY, authToken);
62
61
  const returnPath = new URLSearchParams(window.location.search).get("returnPath");
63
62
  window.location.href = returnPath ? returnPath : dashboardUrl;
64
63
  }
@@ -72,7 +71,7 @@ function AuthProvider({ children, config }) {
72
71
  if (!navigator.onLine) {
73
72
  try {
74
73
  const payload = (0, utils_1.parseJwt)(token);
75
- setUser(payload === null || payload === void 0 ? void 0 : payload.user);
74
+ setUser(payload?.user);
76
75
  setIsAuthenticated(true);
77
76
  }
78
77
  catch (e) {
@@ -89,7 +88,7 @@ function AuthProvider({ children, config }) {
89
88
  }
90
89
  catch (err) {
91
90
  console.warn("[ecp-auth] Failed to fetch user:", err);
92
- await logout("session_expired");
91
+ await logout("server_unavailable");
93
92
  }
94
93
  finally {
95
94
  setLoading(false);
@@ -143,24 +142,30 @@ function AuthProvider({ children, config }) {
143
142
  }
144
143
  }
145
144
  async function logout(reason = "", params) {
146
- var _a;
145
+ let logoutSucceeded = false;
147
146
  try {
148
147
  await (0, api_1.apiFactory)("/logout");
148
+ logoutSucceeded = true;
149
149
  }
150
150
  catch (err) {
151
- console.log(err);
151
+ if ([401, 403].includes(err.status)) {
152
+ logoutSucceeded = true;
153
+ }
154
+ console.error("Logout failed:", err);
152
155
  }
153
- finally {
154
- localStorage.removeItem(refresh_helper_1.AUTH_TOKEN_KEY);
155
- localStorage.removeItem(refresh_helper_1.LEADING_TAB_KEY);
156
- localStorage.removeItem(refresh_helper_1.AUTH_RETRY_STATE_KEY);
157
- await ((_a = config === null || config === void 0 ? void 0 : config.cookieOverride) === null || _a === void 0 ? void 0 : _a.call(config, ""));
156
+ const forceLogout = ["user_logout", "server_unavailable"].includes(reason);
157
+ if (logoutSucceeded || forceLogout) {
158
+ localStorage.removeItem(constants_1.AUTH_TOKEN_KEY);
159
+ localStorage.removeItem(constants_1.LEADING_TAB_KEY);
160
+ localStorage.removeItem(constants_1.AUTH_RETRY_STATE_KEY);
161
+ await config?.cookieOverride?.("");
158
162
  const fullParams = new URLSearchParams({
163
+ returnPath: returnPathRaw,
159
164
  reason,
160
165
  ...params,
161
166
  }).toString();
162
167
  if (typeof window !== "undefined") {
163
- window.location.href = `${loginUrl}?${returnPath}&${fullParams}`;
168
+ window.location.href = `${loginUrl}?${fullParams}`;
164
169
  }
165
170
  }
166
171
  }
@@ -175,9 +180,8 @@ function AuthProvider({ children, config }) {
175
180
  return res;
176
181
  },
177
182
  onTokenRefreshed: (authToken, tempRefreshToken) => {
178
- var _a;
179
- localStorage.setItem(refresh_helper_1.AUTH_TOKEN_KEY, authToken);
180
- (_a = config === null || config === void 0 ? void 0 : config.cookieOverride) === null || _a === void 0 ? void 0 : _a.call(config, tempRefreshToken);
183
+ localStorage.setItem(constants_1.AUTH_TOKEN_KEY, authToken);
184
+ config?.cookieOverride?.(tempRefreshToken);
181
185
  },
182
186
  revalidateUserWhenOnline: fetchCurrentUser,
183
187
  onRefreshFailed: async () => await logout("session_expired"),
@@ -198,6 +202,7 @@ function AuthProvider({ children, config }) {
198
202
  logout: (params) => logout("user_logout", params),
199
203
  hasPermission: (permission) => (0, utils_1.hasPermission)(user, permission),
200
204
  revalidate: () => startTransition(fetchCurrentUser),
205
+ getToken: () => (0, utils_1.getClientToken)(),
201
206
  } }, children)));
202
207
  }
203
208
  function useAuth() {
@@ -4,6 +4,7 @@ exports.useTokenRefresher = useTokenRefresher;
4
4
  const utils_1 = require("../lib/utils");
5
5
  const react_1 = require("react");
6
6
  const refresh_helper_1 = require("../lib/refresh-helper");
7
+ const constants_1 = require("../lib/constants");
7
8
  function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefreshed, revalidateUserWhenOnline, config, }) {
8
9
  const refreshing = (0, react_1.useRef)(false);
9
10
  const interval = (0, react_1.useRef)(null);
@@ -26,7 +27,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
26
27
  refreshing.current = true;
27
28
  const token = (0, utils_1.getClientToken)();
28
29
  if (!token && !isExcluded) {
29
- onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
30
+ onRefreshFailed?.();
30
31
  refreshing.current = false;
31
32
  return;
32
33
  }
@@ -40,7 +41,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
40
41
  const payload = (0, utils_1.parseJwt)(token);
41
42
  const expiresAt = payload.exp * 1000;
42
43
  const timeLeft = expiresAt - Date.now();
43
- if (timeLeft < refresh_helper_1.thresholdMs) {
44
+ if (timeLeft < constants_1.thresholdMs) {
44
45
  shouldRefresh = true;
45
46
  }
46
47
  else {
@@ -49,22 +50,22 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
49
50
  }
50
51
  catch (e) {
51
52
  console.warn("[ecp-auth] Invalid token format, skipping refresh.");
52
- onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
53
+ onRefreshFailed?.();
53
54
  }
54
55
  if (shouldRefresh) {
55
56
  const { authToken, tempRefreshToken } = await refreshTokenRequest();
56
57
  if (!authToken) {
57
- onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
58
+ onRefreshFailed?.();
58
59
  }
59
60
  else {
60
- onTokenRefreshed === null || onTokenRefreshed === void 0 ? void 0 : onTokenRefreshed(authToken, tempRefreshToken);
61
+ onTokenRefreshed?.(authToken, tempRefreshToken);
61
62
  }
62
63
  }
63
64
  }
64
65
  catch (err) {
65
66
  const success = await (0, refresh_helper_1.tryRefreshWithRetry)(refreshTokenRequest);
66
67
  if (!success) {
67
- onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
68
+ onRefreshFailed?.();
68
69
  console.warn("[ecp-auth] All retry attempts failed, triggering logout.");
69
70
  }
70
71
  }
@@ -74,7 +75,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
74
75
  }
75
76
  (0, react_1.useEffect)(() => {
76
77
  refreshIfNeeded(); // run once on mount
77
- interval.current = setInterval(refreshIfNeeded, refresh_helper_1.intervalMs);
78
+ interval.current = setInterval(refreshIfNeeded, constants_1.intervalMs);
78
79
  function onVisible() {
79
80
  if (document.visibilityState === "visible") {
80
81
  refreshIfNeeded();
@@ -96,11 +97,10 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
96
97
  }, []);
97
98
  (0, react_1.useEffect)(() => {
98
99
  function onStorage(e) {
99
- if (e.key === refresh_helper_1.LEADING_TAB_KEY) {
100
+ if (e.key === constants_1.LEADING_TAB_KEY) {
100
101
  // If leader changes, check if we need to act
101
- // Optionally, you can trigger a refresh of local state here
102
102
  }
103
- if (e.key === refresh_helper_1.AUTH_RETRY_STATE_KEY) {
103
+ if (e.key === constants_1.AUTH_RETRY_STATE_KEY) {
104
104
  // Optionally, react to retry state changes
105
105
  }
106
106
  }
@@ -112,7 +112,7 @@ function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefres
112
112
  return;
113
113
  const renewInterval = setInterval(() => {
114
114
  (0, refresh_helper_1.renewLeadership)();
115
- }, refresh_helper_1.LEADER_TIMEOUT / 2); // Renew halfway before timeout
115
+ }, constants_1.LEADER_TIMEOUT / 2); // Renew halfway before timeout
116
116
  return () => clearInterval(renewInterval);
117
117
  }, [refresh_helper_1.isLeader]);
118
118
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from "./contexts/auth-provider";
2
2
  export * from "./components/message";
3
- export { buildRouteContext } from "./lib/utils";
4
- export type { AuthConfig, Project, Service, ServiceType, User, Role, Status, NewRole, UpdateUser, GeneralResponse, FormResponse, VerifyUser, VerifyUserResponse, ResetPasswordWithToken, ResetPasswordWithUserId, Signup, Login, Otp, } from "./types";
3
+ export { buildRouteContext, getClientToken as getToken, hasPermission, } from "./lib/utils";
4
+ export type { AuthConfig, User, Role, Status, NewRole, UpdateUser, GeneralResponse, FormResponse, VerifyUser, VerifyUserResponse, ResetPasswordWithToken, ResetPasswordWithUserId, CreateUser, Login, } from "./types";
package/dist/index.js CHANGED
@@ -14,8 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.buildRouteContext = void 0;
17
+ exports.hasPermission = exports.getToken = exports.buildRouteContext = void 0;
18
18
  __exportStar(require("./contexts/auth-provider"), exports);
19
19
  __exportStar(require("./components/message"), exports);
20
20
  var utils_1 = require("./lib/utils");
21
21
  Object.defineProperty(exports, "buildRouteContext", { enumerable: true, get: function () { return utils_1.buildRouteContext; } });
22
+ Object.defineProperty(exports, "getToken", { enumerable: true, get: function () { return utils_1.getClientToken; } });
23
+ Object.defineProperty(exports, "hasPermission", { enumerable: true, get: function () { return utils_1.hasPermission; } });
@@ -5,34 +5,49 @@ const error_1 = require("./error");
5
5
  const utils_1 = require("./utils");
6
6
  // CLIENT: browser-side calls using public token
7
7
  async function apiClient(path, options) {
8
- const [apiUrl, apiKey] = atob((0, utils_1.getPublicTokenEnv)()).split("$");
9
- if (!apiUrl || !apiKey) {
8
+ const [apiUrl, version, workspaceId, apiKey] = atob((0, utils_1.getPublicTokenEnv)()).split("$");
9
+ if (!apiUrl || !apiKey || !version || !workspaceId) {
10
10
  throw new Error("Invalid API URL or API key. Please set ECP_AUTH_PUBLIC_TOKEN (or the VITE_/NEXT_PUBLIC_ equivalent) in your .env file. Check admin dashboard for env variables.");
11
11
  }
12
12
  const token = (0, utils_1.getClientToken)(); // from localStorage
13
- const { method = "POST", body, headers, ...rest } = options !== null && options !== void 0 ? options : {};
14
- const res = await fetch(`${apiUrl}${path}`, {
15
- method,
16
- body: body ? JSON.stringify(body) : undefined,
17
- headers: {
18
- "Content-Type": "application/json",
19
- Authorization: `Bearer ${token}`,
20
- "x-api-key": apiKey,
21
- "x-is-global": process.env.NEXT_PUBLIC_ECP_GLOBAL === "true" ? "true" : "false",
22
- ...headers,
23
- },
24
- credentials: "include",
25
- ...rest,
26
- });
27
- if (!res.ok) {
28
- let errorData;
29
- try {
30
- errorData = await res.json();
13
+ const { method = "POST", body, headers, ...rest } = options ?? {};
14
+ try {
15
+ const res = await fetch(`${apiUrl}${path}`, {
16
+ method,
17
+ body: body ? JSON.stringify(body) : undefined,
18
+ headers: {
19
+ "Content-Type": "application/json",
20
+ Authorization: `Bearer ${token}`,
21
+ "x-api-key": apiKey,
22
+ "x-api-version": version,
23
+ "x-workspace-id": workspaceId,
24
+ "x-is-global": process.env.NEXT_PUBLIC_ECP_GLOBAL === "true" ? "true" : "false",
25
+ ...headers,
26
+ },
27
+ credentials: "include",
28
+ ...rest,
29
+ });
30
+ if (!res.ok) {
31
+ let errorData;
32
+ try {
33
+ errorData = await res.json();
34
+ }
35
+ catch {
36
+ errorData = { message: "Something went wrong." };
37
+ }
38
+ throw new error_1.APIError(errorData.message || "Unexpected error occurred.", errorData, res.status);
31
39
  }
32
- catch {
33
- errorData = { message: "Something went wrong." };
40
+ return (await res.json());
41
+ }
42
+ catch (err) {
43
+ // Handle network errors
44
+ if (err instanceof TypeError) {
45
+ // fetch throws TypeError for network errors
46
+ throw new error_1.APIError("Network error", {
47
+ message: "Network error: could not reach server. Please check your connection or try again later.",
48
+ }, 0);
34
49
  }
35
- throw new error_1.APIError(errorData.message || "Unexpected error occurred.", errorData, res.status);
50
+ // Re-throw other errors
51
+ throw err;
36
52
  }
37
- return (await res.json());
38
53
  }
@@ -6,13 +6,12 @@ const error_1 = require("./error");
6
6
  const server_token_1 = require("../server/server-token");
7
7
  // SERVER: secure-only calls using private key
8
8
  async function apiServer(apiPath, options) {
9
- var _a;
10
- let [apiUrl, apiKey] = atob((_a = process.env.ECP_AUTH_PRIVATE_TOKEN) !== null && _a !== void 0 ? _a : "").split("$");
11
- if ((!apiUrl || !apiKey) && !(options === null || options === void 0 ? void 0 : options.adminCall)) {
9
+ let [apiUrl, version, workspaceId, apiKey] = atob(process.env.ECP_AUTH_PRIVATE_TOKEN ?? "").split("$");
10
+ if ((!apiUrl || !apiKey || !version || !workspaceId) && !options?.adminCall) {
12
11
  throw new Error("Invalid API URL or API key, please set ECP_AUTH_PRIVATE_TOKEN in your .env file. Check admin dashboard for env variables.");
13
12
  }
14
13
  let token = await (0, server_token_1.getServerToken)();
15
- const { method = "POST", body, headers, adminCall, ...rest } = options !== null && options !== void 0 ? options : {};
14
+ const { method = "POST", body, headers, adminCall, ...rest } = options ?? {};
16
15
  const res = await fetch(`${apiUrl}${apiPath}`, {
17
16
  method,
18
17
  body: body ? JSON.stringify(body) : undefined,
@@ -20,6 +19,8 @@ async function apiServer(apiPath, options) {
20
19
  "Content-Type": "application/json",
21
20
  Authorization: `Bearer ${token}`,
22
21
  "x-api-key": apiKey,
22
+ "x-api-version": version,
23
+ "x-workspace-id": workspaceId,
23
24
  "x-is-global": process.env.NEXT_PUBLIC_ECP_GLOBAL === "true" ? "true" : "false",
24
25
  ...headers,
25
26
  },
@@ -0,0 +1,10 @@
1
+ export declare const AUTH_TOKEN_KEY = "_ecp_auth_token";
2
+ export declare const AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
3
+ export declare const LEADING_TAB_KEY = "_ecp_leading_tab";
4
+ export declare const LEADER_TIMEOUT: number;
5
+ export declare const thresholdMs: number;
6
+ export declare const intervalMs: number;
7
+ export declare const retryDelayMs = 60000;
8
+ export declare const maxRetries = 5;
9
+ export declare const backoffFactor = 2;
10
+ export declare const serverOnlyCall = "This function can only be called on the server";
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serverOnlyCall = exports.backoffFactor = exports.maxRetries = exports.retryDelayMs = exports.intervalMs = exports.thresholdMs = exports.LEADER_TIMEOUT = exports.LEADING_TAB_KEY = exports.AUTH_RETRY_STATE_KEY = exports.AUTH_TOKEN_KEY = void 0;
4
+ exports.AUTH_TOKEN_KEY = "_ecp_auth_token";
5
+ exports.AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
6
+ exports.LEADING_TAB_KEY = "_ecp_leading_tab";
7
+ exports.LEADER_TIMEOUT = 2 * 60 * 1000;
8
+ exports.thresholdMs = 2 * 60_000;
9
+ exports.intervalMs = 1 * 60_000;
10
+ exports.retryDelayMs = 60_000;
11
+ exports.maxRetries = 5;
12
+ exports.backoffFactor = 2;
13
+ exports.serverOnlyCall = "This function can only be called on the server";
package/dist/lib/error.js CHANGED
@@ -11,5 +11,5 @@ class APIError extends Error {
11
11
  }
12
12
  exports.APIError = APIError;
13
13
  function isAPIError(err) {
14
- return (err === null || err === void 0 ? void 0 : err.data) !== undefined;
14
+ return err?.data !== undefined;
15
15
  }
@@ -1,10 +1,4 @@
1
1
  import { UseTokenRefresherOptions } from "../types";
2
- export declare const AUTH_TOKEN_KEY = "_ecp_auth_token";
3
- export declare const AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
4
- export declare const LEADING_TAB_KEY = "_ecp_leading_tab";
5
- export declare const LEADER_TIMEOUT: number;
6
- export declare const thresholdMs: number;
7
- export declare const intervalMs: number;
8
2
  export declare function tryRefreshWithRetry(refreshTokenRequest: UseTokenRefresherOptions["refreshTokenRequest"]): Promise<boolean>;
9
3
  export declare function isLeader(): boolean | undefined;
10
4
  export declare function becomeLeader(): void;
@@ -1,38 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.intervalMs = exports.thresholdMs = exports.LEADER_TIMEOUT = exports.LEADING_TAB_KEY = exports.AUTH_RETRY_STATE_KEY = exports.AUTH_TOKEN_KEY = void 0;
4
3
  exports.tryRefreshWithRetry = tryRefreshWithRetry;
5
4
  exports.isLeader = isLeader;
6
5
  exports.becomeLeader = becomeLeader;
7
6
  exports.renewLeadership = renewLeadership;
8
7
  exports.tryToBecomeLeader = tryToBecomeLeader;
9
8
  const utils_1 = require("./utils");
10
- exports.AUTH_TOKEN_KEY = "_ecp_auth_token";
11
- exports.AUTH_RETRY_STATE_KEY = "_ecp_auth_retry_state";
12
- exports.LEADING_TAB_KEY = "_ecp_leading_tab";
13
- exports.LEADER_TIMEOUT = 2 * 60 * 1000;
14
- exports.thresholdMs = 2 * 60000;
15
- exports.intervalMs = 1 * 60000;
16
- const retryDelayMs = 60000;
17
- const maxRetries = 5;
18
- const backoffFactor = 2;
9
+ const constants_1 = require("./constants");
19
10
  async function tryRefreshWithRetry(refreshTokenRequest) {
20
- var _a;
21
- const savedState = JSON.parse(localStorage.getItem(exports.AUTH_RETRY_STATE_KEY) || "{}");
22
- for (let attempt = (_a = savedState === null || savedState === void 0 ? void 0 : savedState.attemptNumber) !== null && _a !== void 0 ? _a : 1; attempt <= maxRetries; attempt++) {
11
+ const savedState = JSON.parse(localStorage.getItem(constants_1.AUTH_RETRY_STATE_KEY) || "{}");
12
+ for (let attempt = savedState?.attemptNumber ?? 1; attempt <= constants_1.maxRetries; attempt++) {
23
13
  try {
24
14
  await refreshTokenRequest();
25
- localStorage.removeItem(exports.AUTH_RETRY_STATE_KEY);
15
+ localStorage.removeItem(constants_1.AUTH_RETRY_STATE_KEY);
26
16
  return true; // ✅ success!
27
17
  }
28
18
  catch (err) {
29
19
  console.warn(`[ecp-auth] Silent refresh attempt ${attempt} failed`, err);
30
- if (attempt < maxRetries) {
31
- localStorage.setItem(exports.AUTH_RETRY_STATE_KEY, JSON.stringify({
20
+ if (attempt < constants_1.maxRetries) {
21
+ localStorage.setItem(constants_1.AUTH_RETRY_STATE_KEY, JSON.stringify({
32
22
  lastAttempt: Date.now(),
33
23
  attemptNumber: attempt,
34
24
  }));
35
- const delay = retryDelayMs * Math.pow(backoffFactor, attempt - 1);
25
+ const delay = constants_1.retryDelayMs * Math.pow(constants_1.backoffFactor, attempt - 1);
36
26
  console.info(`[ecp-auth] Retrying in ${delay}ms...`);
37
27
  await new Promise((res) => setTimeout(res, delay));
38
28
  }
@@ -50,11 +40,11 @@ const TAB_ID = (() => {
50
40
  function isLeader() {
51
41
  if (typeof localStorage === "undefined")
52
42
  return;
53
- const leaderData = JSON.parse(localStorage.getItem(exports.LEADING_TAB_KEY) || "{}");
43
+ const leaderData = JSON.parse(localStorage.getItem(constants_1.LEADING_TAB_KEY) || "{}");
54
44
  if (!leaderData.id || !leaderData.timestamp)
55
45
  return false;
56
46
  // If leader is stale, allow takeover
57
- if (Date.now() - leaderData.timestamp > exports.LEADER_TIMEOUT)
47
+ if (Date.now() - leaderData.timestamp > constants_1.LEADER_TIMEOUT)
58
48
  return false;
59
49
  return leaderData.id === TAB_ID;
60
50
  }
@@ -62,7 +52,7 @@ function becomeLeader() {
62
52
  const authToken = (0, utils_1.getClientToken)();
63
53
  if (!authToken)
64
54
  return;
65
- localStorage.setItem(exports.LEADING_TAB_KEY, JSON.stringify({ id: TAB_ID, timestamp: Date.now() }));
55
+ localStorage.setItem(constants_1.LEADING_TAB_KEY, JSON.stringify({ id: TAB_ID, timestamp: Date.now() }));
66
56
  }
67
57
  function renewLeadership() {
68
58
  // Called periodically by leader to keep leadership
@@ -73,8 +63,8 @@ function renewLeadership() {
73
63
  function tryToBecomeLeader() {
74
64
  if (typeof localStorage === "undefined")
75
65
  return;
76
- const leaderData = JSON.parse(localStorage.getItem(exports.LEADING_TAB_KEY) || "{}");
77
- if (!leaderData.id || Date.now() - leaderData.timestamp > exports.LEADER_TIMEOUT) {
66
+ const leaderData = JSON.parse(localStorage.getItem(constants_1.LEADING_TAB_KEY) || "{}");
67
+ if (!leaderData.id || Date.now() - leaderData.timestamp > constants_1.LEADER_TIMEOUT) {
78
68
  becomeLeader();
79
69
  return true;
80
70
  }
@@ -1,4 +1,4 @@
1
- import { AuthConfig, User } from "../types";
1
+ import { AuthConfig, QueryOpts, User } from "../types";
2
2
  export declare function cn(...classes: (string | false | null | undefined)[]): string;
3
3
  export declare function unstuckPointerEvents(): void;
4
4
  export declare function buildRouteContext(config?: AuthConfig, currentPath?: string): {
@@ -7,6 +7,7 @@ export declare function buildRouteContext(config?: AuthConfig, currentPath?: str
7
7
  dashboardUrl: string;
8
8
  resetPasswordUrl: string;
9
9
  returnPath: string;
10
+ returnPathRaw: string;
10
11
  currentPath: string;
11
12
  excludedPaths: string[];
12
13
  isExcluded: boolean;
@@ -14,6 +15,7 @@ export declare function buildRouteContext(config?: AuthConfig, currentPath?: str
14
15
  export declare function getClientToken(): string;
15
16
  export declare function hasPermission(user: User | null, permission: string): boolean;
16
17
  export declare function parseMessage(message: string): string;
18
+ export declare function parseGroupId(data?: QueryOpts["groupIds"]): string;
17
19
  export declare function parseJwt(token: string): any;
18
20
  export declare function isOtpAvailable(): boolean;
19
21
  export declare function shouldPassFullUserObject(): boolean;
package/dist/lib/utils.js CHANGED
@@ -6,11 +6,13 @@ exports.buildRouteContext = buildRouteContext;
6
6
  exports.getClientToken = getClientToken;
7
7
  exports.hasPermission = hasPermission;
8
8
  exports.parseMessage = parseMessage;
9
+ exports.parseGroupId = parseGroupId;
9
10
  exports.parseJwt = parseJwt;
10
11
  exports.isOtpAvailable = isOtpAvailable;
11
12
  exports.shouldPassFullUserObject = shouldPassFullUserObject;
12
13
  exports.getPublicTokenEnv = getPublicTokenEnv;
13
14
  exports.getPrivateTokenEnv = getPrivateTokenEnv;
15
+ const constants_1 = require("./constants");
14
16
  function cn(...classes) {
15
17
  return classes.filter(Boolean).join(" ");
16
18
  }
@@ -30,7 +32,6 @@ const defaultAuthConfig = {
30
32
  excludedPaths: [],
31
33
  };
32
34
  function buildRouteContext(config, currentPath = "") {
33
- var _a;
34
35
  const finalConfig = { ...defaultAuthConfig, ...config };
35
36
  const hostOverride = finalConfig.hostOverride;
36
37
  currentPath =
@@ -50,7 +51,7 @@ function buildRouteContext(config, currentPath = "") {
50
51
  const excludedPaths = [
51
52
  normalize(loginUrl),
52
53
  normalize(signupUrl),
53
- ...((_a = config === null || config === void 0 ? void 0 : config.excludedPaths) !== null && _a !== void 0 ? _a : []),
54
+ ...(config?.excludedPaths ?? []),
54
55
  ];
55
56
  try {
56
57
  if (resetPasswordUrl) {
@@ -62,15 +63,16 @@ function buildRouteContext(config, currentPath = "") {
62
63
  }
63
64
  const normalizedCurrentPath = normalize(currentPath);
64
65
  const isExcluded = excludedPaths.includes(normalizedCurrentPath);
65
- const returnPath = typeof window !== "undefined"
66
- ? encodeURIComponent(`${window.location.pathname}${window.location.search}`)
67
- : encodeURIComponent(currentPath);
66
+ const returnPathRaw = typeof window !== "undefined"
67
+ ? `${window.location.pathname}${window.location.search}`
68
+ : currentPath;
68
69
  return {
69
70
  loginUrl,
70
71
  signupUrl,
71
72
  dashboardUrl,
72
73
  resetPasswordUrl,
73
- returnPath,
74
+ returnPath: encodeURIComponent(returnPathRaw),
75
+ returnPathRaw,
74
76
  currentPath: normalizedCurrentPath,
75
77
  excludedPaths,
76
78
  isExcluded,
@@ -78,34 +80,38 @@ function buildRouteContext(config, currentPath = "") {
78
80
  }
79
81
  function getAbsoluteUrl(pathOrUrl, hostOverride) {
80
82
  try {
81
- const url = new URL(pathOrUrl, hostOverride !== null && hostOverride !== void 0 ? hostOverride : "");
83
+ const url = new URL(pathOrUrl, hostOverride ?? "");
82
84
  return url.toString();
83
85
  }
84
86
  catch {
85
87
  if (typeof window !== "undefined") {
86
- const base = hostOverride !== null && hostOverride !== void 0 ? hostOverride : window.location.origin;
88
+ const base = hostOverride ?? window.location.origin;
87
89
  return new URL(pathOrUrl.replace(/^\/+/, "/"), base).toString();
88
90
  }
89
91
  return `http://localhost${pathOrUrl.startsWith("/") ? "" : "/"}${pathOrUrl}`;
90
92
  }
91
93
  }
92
94
  function getClientToken() {
93
- var _a;
94
95
  return typeof localStorage !== "undefined"
95
- ? ((_a = localStorage.getItem("_ecp_auth_token")) !== null && _a !== void 0 ? _a : "")
96
+ ? localStorage.getItem(constants_1.AUTH_TOKEN_KEY) || ""
96
97
  : "";
97
98
  }
98
99
  function hasPermission(user, permission) {
99
- var _a;
100
100
  if (!user)
101
101
  return false;
102
- return ((_a = user.role.permissions) === null || _a === void 0 ? void 0 : _a.includes(permission)) || user.isSuperAdmin;
102
+ return user.role.permissions?.includes(permission) || user.isSuperAdmin;
103
103
  }
104
104
  function parseMessage(message) {
105
105
  return message == "fetch failed" || message == "Failed to fetch"
106
106
  ? "Please check your internet connection"
107
107
  : message;
108
108
  }
109
+ function parseGroupId(data) {
110
+ const query = data
111
+ ?.map((id) => id ?? "null") // keep null as "null"
112
+ .join(",") || "";
113
+ return query ? `?groupIds=${encodeURIComponent(query)}` : "";
114
+ }
109
115
  function base64UrlDecode(b64url) {
110
116
  // Replace URL-safe chars
111
117
  let base64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
@@ -143,6 +149,5 @@ function getPublicTokenEnv() {
143
149
  "");
144
150
  }
145
151
  function getPrivateTokenEnv() {
146
- var _a;
147
- return (_a = process.env.ECP_AUTH_PRIVATE_TOKEN) !== null && _a !== void 0 ? _a : "";
152
+ return process.env.ECP_AUTH_PRIVATE_TOKEN ?? "";
148
153
  }
@@ -1,7 +1,8 @@
1
1
  export * from "./toggle-2fa";
2
2
  export * from "./reset-password";
3
- export * from "./users-accounts";
4
- export * from "./cookie";
3
+ export * from "./user";
4
+ export * from "./next-cookie-override";
5
5
  export * from "./server-session";
6
6
  export * from "./server-token";
7
7
  export * from "./role";
8
+ export { hasPermission } from "../lib/utils";
@@ -14,10 +14,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.hasPermission = void 0;
17
18
  __exportStar(require("./toggle-2fa"), exports);
18
19
  __exportStar(require("./reset-password"), exports);
19
- __exportStar(require("./users-accounts"), exports);
20
- __exportStar(require("./cookie"), exports);
20
+ __exportStar(require("./user"), exports);
21
+ __exportStar(require("./next-cookie-override"), exports);
21
22
  __exportStar(require("./server-session"), exports);
22
23
  __exportStar(require("./server-token"), exports);
23
24
  __exportStar(require("./role"), exports);
25
+ var utils_1 = require("../lib/utils");
26
+ Object.defineProperty(exports, "hasPermission", { enumerable: true, get: function () { return utils_1.hasPermission; } });
@@ -1,17 +1,17 @@
1
- "use server";
2
1
  "use strict";
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.setCookie = setCookie;
4
+ const constants_1 = require("../lib/constants");
5
5
  async function setCookie(value) {
6
6
  try {
7
7
  // Dynamically import to avoid bundling next in non-Next environments
8
8
  const { cookies } = require("next/headers");
9
9
  const cookieStore = await cookies();
10
10
  if (!value) {
11
- cookieStore.delete("_ecp_auth_token");
11
+ cookieStore.delete(constants_1.AUTH_TOKEN_KEY);
12
12
  }
13
13
  else {
14
- cookieStore.set("_ecp_auth_token", value, {
14
+ cookieStore.set(constants_1.AUTH_TOKEN_KEY, value, {
15
15
  httpOnly: true,
16
16
  sameSite: "lax",
17
17
  secure: process.env.NODE_ENV === "production",
@@ -6,8 +6,12 @@ exports.changePassword = changePassword;
6
6
  const utils_1 = require("../lib/utils");
7
7
  const api_1 = require("../lib/api");
8
8
  const error_1 = require("../lib/error");
9
+ const constants_1 = require("../lib/constants");
9
10
  async function resetPassword(data) {
10
11
  try {
12
+ if (typeof window !== "undefined") {
13
+ throw new Error(constants_1.serverOnlyCall);
14
+ }
11
15
  const res = await (0, api_1.apiFactory)("/reset-password", {
12
16
  body: { ...data },
13
17
  });
@@ -22,6 +26,9 @@ async function resetPassword(data) {
22
26
  }
23
27
  async function changePassword(data) {
24
28
  try {
29
+ if (typeof window !== "undefined") {
30
+ throw new Error(constants_1.serverOnlyCall);
31
+ }
25
32
  const res = await (0, api_1.apiFactory)("/change-password", {
26
33
  method: "PUT",
27
34
  body: {
@@ -1,7 +1,5 @@
1
- import { Role, NewRole, FormResponse } from "../types";
2
- export declare function getRoles(options?: {
3
- groupId: string;
4
- }): Promise<Role[]>;
1
+ import { Role, NewRole, FormResponse, QueryOpts } from "../types";
2
+ export declare function getRoles(options?: QueryOpts): Promise<Role[]>;
5
3
  export declare function addRole(role: NewRole): Promise<FormResponse<Role>>;
6
4
  export declare function updateRole(roleId: string, role: NewRole): Promise<FormResponse<Role>>;
7
5
  export declare function assignPermission(roleId: string, permissions: string[]): Promise<FormResponse<Role>>;