@explita/cloud-auth-client 0.0.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/README.md +238 -0
- package/dist/components/change-password.d.ts +11 -0
- package/dist/components/change-password.js +101 -0
- package/dist/components/loader.d.ts +2 -0
- package/dist/components/loader.js +11 -0
- package/dist/components/logged-in.d.ts +3 -0
- package/dist/components/logged-in.js +9 -0
- package/dist/components/logged-out.d.ts +3 -0
- package/dist/components/logged-out.js +9 -0
- package/dist/components/login-form.d.ts +2 -0
- package/dist/components/login-form.js +102 -0
- package/dist/components/message.d.ts +6 -0
- package/dist/components/message.js +15 -0
- package/dist/components/must-login.d.ts +4 -0
- package/dist/components/must-login.js +23 -0
- package/dist/components/optional-otp-wrapper.d.ts +3 -0
- package/dist/components/optional-otp-wrapper.js +52 -0
- package/dist/components/optional-otp.d.ts +3 -0
- package/dist/components/optional-otp.js +70 -0
- package/dist/components/reset-password.d.ts +8 -0
- package/dist/components/reset-password.js +118 -0
- package/dist/components/roles.d.ts +6 -0
- package/dist/components/roles.js +54 -0
- package/dist/components/settings.d.ts +9 -0
- package/dist/components/settings.js +26 -0
- package/dist/components/signup-form.d.ts +11 -0
- package/dist/components/signup-form.js +138 -0
- package/dist/components/toggle-2fa.d.ts +7 -0
- package/dist/components/toggle-2fa.js +81 -0
- package/dist/components/toggle-account-status.d.ts +11 -0
- package/dist/components/toggle-account-status.js +91 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +68 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.js +65 -0
- package/dist/components/ui/dialog.d.ts +15 -0
- package/dist/components/ui/dialog.js +86 -0
- package/dist/components/ui/dropdown-menu.d.ts +11 -0
- package/dist/components/ui/dropdown-menu.js +63 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/input.js +41 -0
- package/dist/components/ui/label.d.ts +3 -0
- package/dist/components/ui/label.js +42 -0
- package/dist/components/ui/switch.d.ts +4 -0
- package/dist/components/ui/switch.js +44 -0
- package/dist/components/user-card.d.ts +10 -0
- package/dist/components/user-card.js +67 -0
- package/dist/components/x-icon.d.ts +2 -0
- package/dist/components/x-icon.js +11 -0
- package/dist/contexts/auth-provider.d.ts +4 -0
- package/dist/contexts/auth-provider.js +208 -0
- package/dist/hooks/use-token-refresher.d.ts +2 -0
- package/dist/hooks/use-token-refresher.js +118 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/lib/api-client.d.ts +2 -0
- package/dist/lib/api-client.js +38 -0
- package/dist/lib/api-server.d.ts +2 -0
- package/dist/lib/api-server.js +39 -0
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.js +10 -0
- package/dist/lib/error.d.ts +6 -0
- package/dist/lib/error.js +15 -0
- package/dist/lib/refresh-helper.d.ts +12 -0
- package/dist/lib/refresh-helper.js +82 -0
- package/dist/lib/utils.d.ts +21 -0
- package/dist/lib/utils.js +148 -0
- package/dist/server/cookie.d.ts +1 -0
- package/dist/server/cookie.js +29 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +23 -0
- package/dist/server/reset-password.d.ts +3 -0
- package/dist/server/reset-password.js +41 -0
- package/dist/server/role.d.ts +7 -0
- package/dist/server/role.js +63 -0
- package/dist/server/server-session.d.ts +8 -0
- package/dist/server/server-session.js +31 -0
- package/dist/server/server-token.d.ts +4 -0
- package/dist/server/server-token.js +39 -0
- package/dist/server/toggle-2fa.d.ts +4 -0
- package/dist/server/toggle-2fa.js +33 -0
- package/dist/server/users-accounts.d.ts +11 -0
- package/dist/server/users-accounts.js +94 -0
- package/dist/styles.css +1799 -0
- package/dist/types.d.ts +284 -0
- package/dist/types.js +2 -0
- package/dist/ui/index.d.ts +11 -0
- package/dist/ui/index.js +27 -0
- package/package.json +65 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AuthProvider = AuthProvider;
|
|
37
|
+
exports.useAuth = useAuth;
|
|
38
|
+
const react_1 = __importStar(require("react"));
|
|
39
|
+
const utils_1 = require("../lib/utils");
|
|
40
|
+
const api_1 = require("../lib/api");
|
|
41
|
+
const loader_1 = require("../components/loader");
|
|
42
|
+
const error_1 = require("../lib/error");
|
|
43
|
+
const use_token_refresher_1 = require("../hooks/use-token-refresher");
|
|
44
|
+
const optional_otp_wrapper_1 = require("../components/optional-otp-wrapper");
|
|
45
|
+
const refresh_helper_1 = require("../lib/refresh-helper");
|
|
46
|
+
const AuthContext = (0, react_1.createContext)(undefined);
|
|
47
|
+
function AuthProvider({ children, config }) {
|
|
48
|
+
const [twoFAData, setTwoFAData] = (0, react_1.useState)(null);
|
|
49
|
+
const [user, setUser] = (0, react_1.useState)(null);
|
|
50
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
51
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
52
|
+
const [isAuthenticated, setIsAuthenticated] = (0, react_1.useState)(false);
|
|
53
|
+
const [transition, startTransition] = (0, react_1.useTransition)();
|
|
54
|
+
const withFullObject = (0, utils_1.shouldPassFullUserObject)();
|
|
55
|
+
const computedRouteContext = (0, utils_1.buildRouteContext)(config);
|
|
56
|
+
const { loginUrl, resetPasswordUrl, dashboardUrl, returnPath, isExcluded } = computedRouteContext;
|
|
57
|
+
const { disableLoading = false } = config || {};
|
|
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);
|
|
62
|
+
const returnPath = new URLSearchParams(window.location.search).get("returnPath");
|
|
63
|
+
window.location.href = returnPath ? returnPath : dashboardUrl;
|
|
64
|
+
}
|
|
65
|
+
const fetchCurrentUser = (0, react_1.useCallback)(async () => {
|
|
66
|
+
const token = (0, utils_1.getClientToken)();
|
|
67
|
+
if (!token && !isExcluded) {
|
|
68
|
+
return await logout("session_expired");
|
|
69
|
+
}
|
|
70
|
+
if (!token)
|
|
71
|
+
return;
|
|
72
|
+
if (!navigator.onLine) {
|
|
73
|
+
try {
|
|
74
|
+
const payload = (0, utils_1.parseJwt)(token);
|
|
75
|
+
setUser(payload === null || payload === void 0 ? void 0 : payload.user);
|
|
76
|
+
setIsAuthenticated(true);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.warn("[ecp-auth] Invalid token format:", e);
|
|
80
|
+
}
|
|
81
|
+
console.warn("[ecp-auth] Offline — skipping fetchCurrentUser()");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
setLoading(true);
|
|
85
|
+
try {
|
|
86
|
+
const res = await (0, api_1.apiFactory)("/me", { method: "GET" });
|
|
87
|
+
setUser(res.user);
|
|
88
|
+
setIsAuthenticated(true);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.warn("[ecp-auth] Failed to fetch user:", err);
|
|
92
|
+
await logout("session_expired");
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
setLoading(false);
|
|
96
|
+
}
|
|
97
|
+
}, []);
|
|
98
|
+
(0, react_1.useEffect)(() => {
|
|
99
|
+
fetchCurrentUser();
|
|
100
|
+
}, []);
|
|
101
|
+
async function login(credentials) {
|
|
102
|
+
setError(null);
|
|
103
|
+
setLoading(true);
|
|
104
|
+
try {
|
|
105
|
+
const res = await (0, api_1.apiFactory)("/login", {
|
|
106
|
+
body: {
|
|
107
|
+
...credentials,
|
|
108
|
+
withFullObject,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
const { authToken, user, with2fa } = res;
|
|
112
|
+
if (authToken && !with2fa) {
|
|
113
|
+
setIsAuthenticated(true);
|
|
114
|
+
await redirectAfterLogin(authToken, res.tempRefreshToken);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
setTwoFAData(user);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
if ((0, error_1.isAPIError)(err)) {
|
|
122
|
+
setError(err.data);
|
|
123
|
+
return err.data;
|
|
124
|
+
}
|
|
125
|
+
return { message: err.message, status: "failure" };
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
setLoading(false);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function sendPasswordResetRequest(email) {
|
|
132
|
+
try {
|
|
133
|
+
const res = await (0, api_1.apiFactory)("/reset-password", {
|
|
134
|
+
body: { email, passwordResetLink: resetPasswordUrl },
|
|
135
|
+
});
|
|
136
|
+
return res;
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
if ((0, error_1.isAPIError)(err)) {
|
|
140
|
+
return err.data;
|
|
141
|
+
}
|
|
142
|
+
return { message: err.message, status: "failure" };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function logout(reason = "", params) {
|
|
146
|
+
var _a;
|
|
147
|
+
try {
|
|
148
|
+
await (0, api_1.apiFactory)("/logout");
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.log(err);
|
|
152
|
+
}
|
|
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, ""));
|
|
158
|
+
const fullParams = new URLSearchParams({
|
|
159
|
+
reason,
|
|
160
|
+
...params,
|
|
161
|
+
}).toString();
|
|
162
|
+
if (typeof window !== "undefined") {
|
|
163
|
+
window.location.href = `${loginUrl}?${returnPath}&${fullParams}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
(0, use_token_refresher_1.useTokenRefresher)({
|
|
168
|
+
config,
|
|
169
|
+
refreshTokenRequest: async () => {
|
|
170
|
+
const res = await (0, api_1.apiFactory)("/refresh", {
|
|
171
|
+
body: {
|
|
172
|
+
withFullObject,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
return res;
|
|
176
|
+
},
|
|
177
|
+
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);
|
|
181
|
+
},
|
|
182
|
+
revalidateUserWhenOnline: fetchCurrentUser,
|
|
183
|
+
onRefreshFailed: async () => await logout("session_expired"),
|
|
184
|
+
});
|
|
185
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
186
|
+
react_1.default.createElement(optional_otp_wrapper_1.OptionalOTPWrapper, { user: twoFAData, onVerified: redirectAfterLogin, onCanceled: () => {
|
|
187
|
+
setTwoFAData(null);
|
|
188
|
+
} }),
|
|
189
|
+
!disableLoading && loading && react_1.default.createElement(loader_1.Loader, null),
|
|
190
|
+
react_1.default.createElement(AuthContext.Provider, { value: {
|
|
191
|
+
user,
|
|
192
|
+
authLoading: loading,
|
|
193
|
+
isAuthenticated,
|
|
194
|
+
error,
|
|
195
|
+
computedRouteContext,
|
|
196
|
+
login,
|
|
197
|
+
sendPasswordResetRequest,
|
|
198
|
+
logout: (params) => logout("user_logout", params),
|
|
199
|
+
hasPermission: (permission) => (0, utils_1.hasPermission)(user, permission),
|
|
200
|
+
revalidate: () => startTransition(fetchCurrentUser),
|
|
201
|
+
} }, children)));
|
|
202
|
+
}
|
|
203
|
+
function useAuth() {
|
|
204
|
+
const ctx = (0, react_1.useContext)(AuthContext);
|
|
205
|
+
if (!ctx)
|
|
206
|
+
throw new Error("useAuth must be used inside AuthProvider");
|
|
207
|
+
return ctx;
|
|
208
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useTokenRefresher = useTokenRefresher;
|
|
4
|
+
const utils_1 = require("../lib/utils");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const refresh_helper_1 = require("../lib/refresh-helper");
|
|
7
|
+
function useTokenRefresher({ refreshTokenRequest, onRefreshFailed, onTokenRefreshed, revalidateUserWhenOnline, config, }) {
|
|
8
|
+
const refreshing = (0, react_1.useRef)(false);
|
|
9
|
+
const interval = (0, react_1.useRef)(null);
|
|
10
|
+
const { isExcluded } = (0, utils_1.buildRouteContext)(config);
|
|
11
|
+
async function refreshIfNeeded() {
|
|
12
|
+
if (!navigator.onLine) {
|
|
13
|
+
console.debug("[ecp-auth] Skipping token refresh: offline");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Only the leader should perform refresh
|
|
17
|
+
if (!(0, refresh_helper_1.isLeader)()) {
|
|
18
|
+
// Try to become leader if leader is stale
|
|
19
|
+
if (!(0, refresh_helper_1.tryToBecomeLeader)())
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Leadership renewal (keep-alive)
|
|
23
|
+
(0, refresh_helper_1.renewLeadership)();
|
|
24
|
+
if (refreshing.current)
|
|
25
|
+
return;
|
|
26
|
+
refreshing.current = true;
|
|
27
|
+
const token = (0, utils_1.getClientToken)();
|
|
28
|
+
if (!token && !isExcluded) {
|
|
29
|
+
onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
|
|
30
|
+
refreshing.current = false;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!token) {
|
|
34
|
+
refreshing.current = false;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
let shouldRefresh = false;
|
|
39
|
+
try {
|
|
40
|
+
const payload = (0, utils_1.parseJwt)(token);
|
|
41
|
+
const expiresAt = payload.exp * 1000;
|
|
42
|
+
const timeLeft = expiresAt - Date.now();
|
|
43
|
+
if (timeLeft < refresh_helper_1.thresholdMs) {
|
|
44
|
+
shouldRefresh = true;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.debug(`[ecp-auth] Token still valid: ${Math.floor(timeLeft / 1000)}s left`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.warn("[ecp-auth] Invalid token format, skipping refresh.");
|
|
52
|
+
onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
|
|
53
|
+
}
|
|
54
|
+
if (shouldRefresh) {
|
|
55
|
+
const { authToken, tempRefreshToken } = await refreshTokenRequest();
|
|
56
|
+
if (!authToken) {
|
|
57
|
+
onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
onTokenRefreshed === null || onTokenRefreshed === void 0 ? void 0 : onTokenRefreshed(authToken, tempRefreshToken);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const success = await (0, refresh_helper_1.tryRefreshWithRetry)(refreshTokenRequest);
|
|
66
|
+
if (!success) {
|
|
67
|
+
onRefreshFailed === null || onRefreshFailed === void 0 ? void 0 : onRefreshFailed();
|
|
68
|
+
console.warn("[ecp-auth] All retry attempts failed, triggering logout.");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
refreshing.current = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
(0, react_1.useEffect)(() => {
|
|
76
|
+
refreshIfNeeded(); // run once on mount
|
|
77
|
+
interval.current = setInterval(refreshIfNeeded, refresh_helper_1.intervalMs);
|
|
78
|
+
function onVisible() {
|
|
79
|
+
if (document.visibilityState === "visible") {
|
|
80
|
+
refreshIfNeeded();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function onOnline() {
|
|
84
|
+
refreshIfNeeded();
|
|
85
|
+
if (typeof revalidateUserWhenOnline === "function") {
|
|
86
|
+
revalidateUserWhenOnline();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
90
|
+
window.addEventListener("online", onOnline);
|
|
91
|
+
return () => {
|
|
92
|
+
clearInterval(interval.current);
|
|
93
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
94
|
+
window.removeEventListener("online", onOnline);
|
|
95
|
+
};
|
|
96
|
+
}, []);
|
|
97
|
+
(0, react_1.useEffect)(() => {
|
|
98
|
+
function onStorage(e) {
|
|
99
|
+
if (e.key === refresh_helper_1.LEADING_TAB_KEY) {
|
|
100
|
+
// If leader changes, check if we need to act
|
|
101
|
+
// Optionally, you can trigger a refresh of local state here
|
|
102
|
+
}
|
|
103
|
+
if (e.key === refresh_helper_1.AUTH_RETRY_STATE_KEY) {
|
|
104
|
+
// Optionally, react to retry state changes
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
window.addEventListener("storage", onStorage);
|
|
108
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
109
|
+
}, []);
|
|
110
|
+
(0, react_1.useEffect)(() => {
|
|
111
|
+
if (!(0, refresh_helper_1.isLeader)())
|
|
112
|
+
return;
|
|
113
|
+
const renewInterval = setInterval(() => {
|
|
114
|
+
(0, refresh_helper_1.renewLeadership)();
|
|
115
|
+
}, refresh_helper_1.LEADER_TIMEOUT / 2); // Renew halfway before timeout
|
|
116
|
+
return () => clearInterval(renewInterval);
|
|
117
|
+
}, [refresh_helper_1.isLeader]);
|
|
118
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from "./contexts/auth-provider";
|
|
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";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.buildRouteContext = void 0;
|
|
18
|
+
__exportStar(require("./contexts/auth-provider"), exports);
|
|
19
|
+
__exportStar(require("./components/message"), exports);
|
|
20
|
+
var utils_1 = require("./lib/utils");
|
|
21
|
+
Object.defineProperty(exports, "buildRouteContext", { enumerable: true, get: function () { return utils_1.buildRouteContext; } });
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiClient = apiClient;
|
|
4
|
+
const error_1 = require("./error");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
// CLIENT: browser-side calls using public token
|
|
7
|
+
async function apiClient(path, options) {
|
|
8
|
+
const [apiUrl, apiKey] = atob((0, utils_1.getPublicTokenEnv)()).split("$");
|
|
9
|
+
if (!apiUrl || !apiKey) {
|
|
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
|
+
}
|
|
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();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
errorData = { message: "Something went wrong." };
|
|
34
|
+
}
|
|
35
|
+
throw new error_1.APIError(errorData.message || "Unexpected error occurred.", errorData, res.status);
|
|
36
|
+
}
|
|
37
|
+
return (await res.json());
|
|
38
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.apiServer = apiServer;
|
|
5
|
+
const error_1 = require("./error");
|
|
6
|
+
const server_token_1 = require("../server/server-token");
|
|
7
|
+
// SERVER: secure-only calls using private key
|
|
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)) {
|
|
12
|
+
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
|
+
}
|
|
14
|
+
let token = await (0, server_token_1.getServerToken)();
|
|
15
|
+
const { method = "POST", body, headers, adminCall, ...rest } = options !== null && options !== void 0 ? options : {};
|
|
16
|
+
const res = await fetch(`${apiUrl}${apiPath}`, {
|
|
17
|
+
method,
|
|
18
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
Authorization: `Bearer ${token}`,
|
|
22
|
+
"x-api-key": apiKey,
|
|
23
|
+
"x-is-global": process.env.NEXT_PUBLIC_ECP_GLOBAL === "true" ? "true" : "false",
|
|
24
|
+
...headers,
|
|
25
|
+
},
|
|
26
|
+
...rest,
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
let errorData;
|
|
30
|
+
try {
|
|
31
|
+
errorData = await res.json();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
errorData = { message: "Something went wrong." };
|
|
35
|
+
}
|
|
36
|
+
throw new error_1.APIError(errorData.message || "Unexpected error occurred.", errorData, res.status);
|
|
37
|
+
}
|
|
38
|
+
return (await res.json());
|
|
39
|
+
}
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiFactory = apiFactory;
|
|
4
|
+
const api_server_1 = require("./api-server");
|
|
5
|
+
const api_client_1 = require("./api-client");
|
|
6
|
+
// Auto-switcher: picks correct one
|
|
7
|
+
function apiFactory(path, options) {
|
|
8
|
+
const isServer = typeof window === "undefined";
|
|
9
|
+
return isServer ? (0, api_server_1.apiServer)(path, options) : (0, api_client_1.apiClient)(path, options);
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APIError = void 0;
|
|
4
|
+
exports.isAPIError = isAPIError;
|
|
5
|
+
class APIError extends Error {
|
|
6
|
+
constructor(message, data, status) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.data = data;
|
|
9
|
+
this.status = status;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.APIError = APIError;
|
|
13
|
+
function isAPIError(err) {
|
|
14
|
+
return (err === null || err === void 0 ? void 0 : err.data) !== undefined;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
export declare function tryRefreshWithRetry(refreshTokenRequest: UseTokenRefresherOptions["refreshTokenRequest"]): Promise<boolean>;
|
|
9
|
+
export declare function isLeader(): boolean | undefined;
|
|
10
|
+
export declare function becomeLeader(): void;
|
|
11
|
+
export declare function renewLeadership(): void;
|
|
12
|
+
export declare function tryToBecomeLeader(): boolean | undefined;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
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
|
+
exports.tryRefreshWithRetry = tryRefreshWithRetry;
|
|
5
|
+
exports.isLeader = isLeader;
|
|
6
|
+
exports.becomeLeader = becomeLeader;
|
|
7
|
+
exports.renewLeadership = renewLeadership;
|
|
8
|
+
exports.tryToBecomeLeader = tryToBecomeLeader;
|
|
9
|
+
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;
|
|
19
|
+
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++) {
|
|
23
|
+
try {
|
|
24
|
+
await refreshTokenRequest();
|
|
25
|
+
localStorage.removeItem(exports.AUTH_RETRY_STATE_KEY);
|
|
26
|
+
return true; // ✅ success!
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
console.warn(`[ecp-auth] Silent refresh attempt ${attempt} failed`, err);
|
|
30
|
+
if (attempt < maxRetries) {
|
|
31
|
+
localStorage.setItem(exports.AUTH_RETRY_STATE_KEY, JSON.stringify({
|
|
32
|
+
lastAttempt: Date.now(),
|
|
33
|
+
attemptNumber: attempt,
|
|
34
|
+
}));
|
|
35
|
+
const delay = retryDelayMs * Math.pow(backoffFactor, attempt - 1);
|
|
36
|
+
console.info(`[ecp-auth] Retrying in ${delay}ms...`);
|
|
37
|
+
await new Promise((res) => setTimeout(res, delay));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return false; // ❌ All retries failed
|
|
42
|
+
}
|
|
43
|
+
const TAB_ID = (() => {
|
|
44
|
+
// Use crypto if available, fallback to timestamp+random
|
|
45
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
46
|
+
return crypto.randomUUID();
|
|
47
|
+
}
|
|
48
|
+
return `${Date.now()}-${Math.random()}`;
|
|
49
|
+
})();
|
|
50
|
+
function isLeader() {
|
|
51
|
+
if (typeof localStorage === "undefined")
|
|
52
|
+
return;
|
|
53
|
+
const leaderData = JSON.parse(localStorage.getItem(exports.LEADING_TAB_KEY) || "{}");
|
|
54
|
+
if (!leaderData.id || !leaderData.timestamp)
|
|
55
|
+
return false;
|
|
56
|
+
// If leader is stale, allow takeover
|
|
57
|
+
if (Date.now() - leaderData.timestamp > exports.LEADER_TIMEOUT)
|
|
58
|
+
return false;
|
|
59
|
+
return leaderData.id === TAB_ID;
|
|
60
|
+
}
|
|
61
|
+
function becomeLeader() {
|
|
62
|
+
const authToken = (0, utils_1.getClientToken)();
|
|
63
|
+
if (!authToken)
|
|
64
|
+
return;
|
|
65
|
+
localStorage.setItem(exports.LEADING_TAB_KEY, JSON.stringify({ id: TAB_ID, timestamp: Date.now() }));
|
|
66
|
+
}
|
|
67
|
+
function renewLeadership() {
|
|
68
|
+
// Called periodically by leader to keep leadership
|
|
69
|
+
if (isLeader()) {
|
|
70
|
+
becomeLeader();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function tryToBecomeLeader() {
|
|
74
|
+
if (typeof localStorage === "undefined")
|
|
75
|
+
return;
|
|
76
|
+
const leaderData = JSON.parse(localStorage.getItem(exports.LEADING_TAB_KEY) || "{}");
|
|
77
|
+
if (!leaderData.id || Date.now() - leaderData.timestamp > exports.LEADER_TIMEOUT) {
|
|
78
|
+
becomeLeader();
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AuthConfig, User } from "../types";
|
|
2
|
+
export declare function cn(...classes: (string | false | null | undefined)[]): string;
|
|
3
|
+
export declare function unstuckPointerEvents(): void;
|
|
4
|
+
export declare function buildRouteContext(config?: AuthConfig, currentPath?: string): {
|
|
5
|
+
loginUrl: string;
|
|
6
|
+
signupUrl: string;
|
|
7
|
+
dashboardUrl: string;
|
|
8
|
+
resetPasswordUrl: string;
|
|
9
|
+
returnPath: string;
|
|
10
|
+
currentPath: string;
|
|
11
|
+
excludedPaths: string[];
|
|
12
|
+
isExcluded: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function getClientToken(): string;
|
|
15
|
+
export declare function hasPermission(user: User | null, permission: string): boolean;
|
|
16
|
+
export declare function parseMessage(message: string): string;
|
|
17
|
+
export declare function parseJwt(token: string): any;
|
|
18
|
+
export declare function isOtpAvailable(): boolean;
|
|
19
|
+
export declare function shouldPassFullUserObject(): boolean;
|
|
20
|
+
export declare function getPublicTokenEnv(): string;
|
|
21
|
+
export declare function getPrivateTokenEnv(): string;
|