@indusaction/cms-auth 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.
- package/README.md +82 -0
- package/dist/index.cjs +558 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +225 -0
- package/dist/index.d.ts +225 -0
- package/dist/index.js +534 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @indusaction/cms-auth
|
|
2
|
+
|
|
3
|
+
Shared OTP + cookie JWT authentication for IndusAction CMS **web** (`campaign-fe`) and **mobile** (`case-fe`).
|
|
4
|
+
|
|
5
|
+
Works with **campaign-be**: HttpOnly `{appSource}_cms_token` cookies, `withCredentials`, 401 → refresh.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @indusaction/cms-auth axios js-cookie antd react react-dom react-router-dom
|
|
11
|
+
|
|
12
|
+
# Optional — wrap the app with ThemeProvider from the UI package for brand colors
|
|
13
|
+
npm install @indusaction/cms-design-system
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import {
|
|
20
|
+
createAuthClient,
|
|
21
|
+
AuthProvider,
|
|
22
|
+
IdentifierForm,
|
|
23
|
+
OtpForm,
|
|
24
|
+
ProtectedRoute,
|
|
25
|
+
GuestRoute,
|
|
26
|
+
MOBILE_SEND_OTP_PATH,
|
|
27
|
+
} from "@indusaction/cms-auth";
|
|
28
|
+
import { ThemeProvider } from "@indusaction/cms-design-system";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
// campaign-fe (web)
|
|
33
|
+
const authClient = createAuthClient({
|
|
34
|
+
baseURL: import.meta.env.VITE_IA_CMS_BASEURL,
|
|
35
|
+
appSource: import.meta.env.VITE_APP_SOURCE, // e.g. campaign-local
|
|
36
|
+
profilePrefix: "campaign",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// case-fe (mobile) — only send-otp path differs
|
|
40
|
+
const caseAuthClient = createAuthClient({
|
|
41
|
+
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
42
|
+
appSource: import.meta.env.VITE_APP_SOURCE, // e.g. case-local
|
|
43
|
+
profilePrefix: "case",
|
|
44
|
+
paths: {
|
|
45
|
+
sendOtp: MOBILE_SEND_OTP_PATH,
|
|
46
|
+
login: "auth/login/",
|
|
47
|
+
refresh: "auth/token/refresh/",
|
|
48
|
+
logout: "auth/logout/",
|
|
49
|
+
},
|
|
50
|
+
loginPath: "/auth/login",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
function App() {
|
|
54
|
+
return (
|
|
55
|
+
<ThemeProvider>
|
|
56
|
+
<AuthProvider
|
|
57
|
+
client={authClient}
|
|
58
|
+
onLoginSuccess={() => {
|
|
59
|
+
window.location.assign("/");
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
{/* routes */}
|
|
63
|
+
</AuthProvider>
|
|
64
|
+
</ThemeProvider>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use `authClient.api` for authenticated API calls (same Axios instance with refresh interceptor).
|
|
70
|
+
|
|
71
|
+
## Package surface
|
|
72
|
+
|
|
73
|
+
| Export | Role |
|
|
74
|
+
|--------|------|
|
|
75
|
+
| `createAuthClient` | Axios + OTP/login/logout/refresh + cookie helpers |
|
|
76
|
+
| `AuthProvider` / `useAuth` | `signIn`, `verifyOtp`, `logout` |
|
|
77
|
+
| `ProtectedRoute` / `GuestRoute` | Cookie-based route guards |
|
|
78
|
+
| `IdentifierForm` / `OtpForm` | Shared login UI (Ant Design; themed via app `ThemeProvider`) |
|
|
79
|
+
| `useAutoLogout` | Optional expiry-cookie logout (case-fe) |
|
|
80
|
+
| `buildCookieKeys` / storage helpers | Low-level cookie/localStorage |
|
|
81
|
+
|
|
82
|
+
App-specific flows (permissions routes, Glific, public Bearer session, offline sync) stay in each app.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Cookies = require('js-cookie');
|
|
4
|
+
var axios = require('axios');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
var reactRouterDom = require('react-router-dom');
|
|
8
|
+
var antd = require('antd');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var Cookies__default = /*#__PURE__*/_interopDefault(Cookies);
|
|
13
|
+
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
14
|
+
|
|
15
|
+
// src/types.ts
|
|
16
|
+
var DEFAULT_AUTH_PATHS = {
|
|
17
|
+
sendOtp: "api/v1/users/send-otp/",
|
|
18
|
+
login: "api/v1/auth/login/",
|
|
19
|
+
refresh: "api/v1/auth/token/refresh/",
|
|
20
|
+
logout: "api/v1/auth/logout/"
|
|
21
|
+
};
|
|
22
|
+
var MOBILE_SEND_OTP_PATH = "users/send-otp-mobile/";
|
|
23
|
+
|
|
24
|
+
// src/cookies.ts
|
|
25
|
+
function buildCookieKeys(appSource, profilePrefix) {
|
|
26
|
+
return {
|
|
27
|
+
isLoggedIn: `${appSource}_is_logged_in`,
|
|
28
|
+
tokenExpiry: `${appSource}_cms_token_expiry`,
|
|
29
|
+
userId: `${profilePrefix}_user_id`,
|
|
30
|
+
userRole: `${profilePrefix}_user_role`,
|
|
31
|
+
userType: `${profilePrefix}_user_type`,
|
|
32
|
+
userFirstName: `${profilePrefix}_user_first_name`,
|
|
33
|
+
userLastName: `${profilePrefix}_user_last_name`,
|
|
34
|
+
userPermissions: `${profilePrefix}_user_permissions`,
|
|
35
|
+
notification: `${profilePrefix}_notification`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function authCookieKeyList(keys) {
|
|
39
|
+
return Object.values(keys);
|
|
40
|
+
}
|
|
41
|
+
function isLoggedIn(keys) {
|
|
42
|
+
return Cookies__default.default.get(keys.isLoggedIn) === "true";
|
|
43
|
+
}
|
|
44
|
+
function getTokenExpiry(keys) {
|
|
45
|
+
const raw = Cookies__default.default.get(keys.tokenExpiry);
|
|
46
|
+
if (!raw) return null;
|
|
47
|
+
const value = Number(raw);
|
|
48
|
+
return Number.isFinite(value) ? value : null;
|
|
49
|
+
}
|
|
50
|
+
function getSessionProfile(keys, permissionsStorageKey) {
|
|
51
|
+
const idx = Cookies__default.default.get(keys.userId);
|
|
52
|
+
const role = Cookies__default.default.get(keys.userRole);
|
|
53
|
+
const userType = Cookies__default.default.get(keys.userType);
|
|
54
|
+
if (!idx || !role || !userType) return null;
|
|
55
|
+
let permissions = [];
|
|
56
|
+
try {
|
|
57
|
+
const raw = localStorage.getItem(permissionsStorageKey);
|
|
58
|
+
if (raw) permissions = JSON.parse(raw);
|
|
59
|
+
} catch {
|
|
60
|
+
permissions = [];
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
idx,
|
|
64
|
+
role,
|
|
65
|
+
userType,
|
|
66
|
+
firstName: Cookies__default.default.get(keys.userFirstName),
|
|
67
|
+
lastName: Cookies__default.default.get(keys.userLastName),
|
|
68
|
+
permissions
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function persistSessionProfile(user, keys, permissionsStorageKey) {
|
|
72
|
+
const permissions = user.user_permissions ?? [];
|
|
73
|
+
localStorage.setItem(permissionsStorageKey, JSON.stringify(permissions));
|
|
74
|
+
Cookies__default.default.set(keys.userId, user.idx);
|
|
75
|
+
Cookies__default.default.set(keys.userRole, user.role);
|
|
76
|
+
Cookies__default.default.set(keys.userType, user.user_type);
|
|
77
|
+
if (user.first_name) Cookies__default.default.set(keys.userFirstName, user.first_name);
|
|
78
|
+
if (user.last_name) Cookies__default.default.set(keys.userLastName, user.last_name);
|
|
79
|
+
if (permissions.length) {
|
|
80
|
+
Cookies__default.default.set(keys.userPermissions, JSON.stringify(permissions));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function clearAuthStorage(keys, permissionsStorageKey, extraClearKeys = []) {
|
|
84
|
+
authCookieKeyList(keys).forEach((key) => Cookies__default.default.remove(key));
|
|
85
|
+
localStorage.removeItem(permissionsStorageKey);
|
|
86
|
+
extraClearKeys.forEach((key) => {
|
|
87
|
+
localStorage.removeItem(key);
|
|
88
|
+
sessionStorage.removeItem(key);
|
|
89
|
+
Cookies__default.default.remove(key);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function joinPath(baseURL, path) {
|
|
93
|
+
const base = baseURL.replace(/\/+$/, "");
|
|
94
|
+
const normalized = path.replace(/^\/+/, "");
|
|
95
|
+
return `${base}/${normalized}`;
|
|
96
|
+
}
|
|
97
|
+
function createAuthClient(options) {
|
|
98
|
+
const paths = {
|
|
99
|
+
...DEFAULT_AUTH_PATHS,
|
|
100
|
+
...options.paths
|
|
101
|
+
};
|
|
102
|
+
const cookieKeys = buildCookieKeys(options.appSource, options.profilePrefix);
|
|
103
|
+
const permissionsStorageKey = options.permissionsStorageKey ?? `${options.profilePrefix}_user_permissions`;
|
|
104
|
+
const extraClearKeys = options.extraClearKeys ?? [];
|
|
105
|
+
const loginPath = options.loginPath ?? "/login";
|
|
106
|
+
const api = axios__default.default.create({
|
|
107
|
+
baseURL: options.baseURL,
|
|
108
|
+
withCredentials: true
|
|
109
|
+
});
|
|
110
|
+
let refreshPromise = null;
|
|
111
|
+
const refresh = async () => {
|
|
112
|
+
if (!refreshPromise) {
|
|
113
|
+
refreshPromise = api.post(paths.refresh).then(() => void 0).finally(() => {
|
|
114
|
+
refreshPromise = null;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return refreshPromise;
|
|
118
|
+
};
|
|
119
|
+
const clearSession = async (clearOptions = {}) => {
|
|
120
|
+
const shouldRedirect = clearOptions.redirect !== false;
|
|
121
|
+
try {
|
|
122
|
+
await api.post(paths.logout);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
clearAuthStorage(cookieKeys, permissionsStorageKey, extraClearKeys);
|
|
126
|
+
options.onSessionCleared?.();
|
|
127
|
+
if (shouldRedirect && typeof window !== "undefined" && window.location.pathname !== loginPath) {
|
|
128
|
+
window.location.replace(loginPath);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
api.interceptors.response.use(
|
|
132
|
+
(response) => response,
|
|
133
|
+
async (error) => {
|
|
134
|
+
const originalRequest = error.config;
|
|
135
|
+
const status = error.response?.status;
|
|
136
|
+
const url = originalRequest?.url ?? "";
|
|
137
|
+
if (status === 401 && originalRequest && !originalRequest._retry && !url.includes(paths.refresh)) {
|
|
138
|
+
originalRequest._retry = true;
|
|
139
|
+
try {
|
|
140
|
+
await refresh();
|
|
141
|
+
return api(originalRequest);
|
|
142
|
+
} catch {
|
|
143
|
+
await clearSession();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return Promise.reject(error);
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
const sendOtp = async (payload) => {
|
|
150
|
+
const { data } = await api.post(paths.sendOtp, payload);
|
|
151
|
+
return data;
|
|
152
|
+
};
|
|
153
|
+
const login = async (payload) => {
|
|
154
|
+
const { data } = await api.post(
|
|
155
|
+
paths.login,
|
|
156
|
+
payload
|
|
157
|
+
);
|
|
158
|
+
const user = data.data;
|
|
159
|
+
persistSessionProfile(user, cookieKeys, permissionsStorageKey);
|
|
160
|
+
return user;
|
|
161
|
+
};
|
|
162
|
+
const logout = async (clearOptions = {}) => {
|
|
163
|
+
await clearSession(clearOptions);
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
api,
|
|
167
|
+
paths,
|
|
168
|
+
cookieKeys,
|
|
169
|
+
permissionsStorageKey,
|
|
170
|
+
options: {
|
|
171
|
+
...options,
|
|
172
|
+
loginPath,
|
|
173
|
+
extraClearKeys
|
|
174
|
+
},
|
|
175
|
+
sendOtp,
|
|
176
|
+
login,
|
|
177
|
+
logout,
|
|
178
|
+
refresh,
|
|
179
|
+
clearSession,
|
|
180
|
+
isLoggedIn: () => isLoggedIn(cookieKeys),
|
|
181
|
+
getTokenExpiry: () => getTokenExpiry(cookieKeys),
|
|
182
|
+
getSessionProfile: () => getSessionProfile(cookieKeys, permissionsStorageKey)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
var AuthContext = react.createContext(null);
|
|
186
|
+
function defaultMapError(error, phase) {
|
|
187
|
+
if (phase === "verifyOtp") return "Invalid OTP";
|
|
188
|
+
if (phase === "logout") return "Error logging out";
|
|
189
|
+
const err = error;
|
|
190
|
+
const data = err?.response?.data?.data;
|
|
191
|
+
if (data && typeof data === "object" && "identifier" in data) {
|
|
192
|
+
const identifier = data.identifier;
|
|
193
|
+
if (Array.isArray(identifier)) return identifier.join("");
|
|
194
|
+
}
|
|
195
|
+
return "An error occurred";
|
|
196
|
+
}
|
|
197
|
+
function AuthProvider({
|
|
198
|
+
client,
|
|
199
|
+
children,
|
|
200
|
+
onLoginSuccess,
|
|
201
|
+
onLogout,
|
|
202
|
+
mapError = defaultMapError
|
|
203
|
+
}) {
|
|
204
|
+
const [status, setStatus] = react.useState("IDLE");
|
|
205
|
+
const [error, setError] = react.useState(null);
|
|
206
|
+
const [user, setUser] = react.useState(null);
|
|
207
|
+
const signIn = react.useCallback(
|
|
208
|
+
async (payload) => {
|
|
209
|
+
try {
|
|
210
|
+
setStatus("PENDING");
|
|
211
|
+
setError(null);
|
|
212
|
+
const response = await client.sendOtp(payload);
|
|
213
|
+
setStatus("SUCCESS");
|
|
214
|
+
return response;
|
|
215
|
+
} catch (err) {
|
|
216
|
+
const message = mapError(err, "signIn");
|
|
217
|
+
setStatus("ERROR");
|
|
218
|
+
setError(message);
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
[client, mapError]
|
|
223
|
+
);
|
|
224
|
+
const verifyOtp = react.useCallback(
|
|
225
|
+
async (payload) => {
|
|
226
|
+
try {
|
|
227
|
+
setStatus("PENDING");
|
|
228
|
+
setError(null);
|
|
229
|
+
const nextUser = await client.login(payload);
|
|
230
|
+
setUser(nextUser);
|
|
231
|
+
setStatus("SUCCESS");
|
|
232
|
+
await onLoginSuccess?.(nextUser);
|
|
233
|
+
return nextUser;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
const message = mapError(err, "verifyOtp");
|
|
236
|
+
setStatus("ERROR");
|
|
237
|
+
setError(message);
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
[client, mapError, onLoginSuccess]
|
|
242
|
+
);
|
|
243
|
+
const logout = react.useCallback(async () => {
|
|
244
|
+
try {
|
|
245
|
+
setStatus("PENDING");
|
|
246
|
+
await client.logout();
|
|
247
|
+
setUser(null);
|
|
248
|
+
setStatus("IDLE");
|
|
249
|
+
setError(null);
|
|
250
|
+
await onLogout?.();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
const message = mapError(err, "logout");
|
|
253
|
+
setStatus("ERROR");
|
|
254
|
+
setError(message);
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
}, [client, mapError, onLogout]);
|
|
258
|
+
const value = react.useMemo(
|
|
259
|
+
() => ({
|
|
260
|
+
client,
|
|
261
|
+
status,
|
|
262
|
+
error,
|
|
263
|
+
user,
|
|
264
|
+
signIn,
|
|
265
|
+
verifyOtp,
|
|
266
|
+
logout,
|
|
267
|
+
isAuthenticated: client.isLoggedIn()
|
|
268
|
+
}),
|
|
269
|
+
[client, status, error, user, signIn, verifyOtp, logout]
|
|
270
|
+
);
|
|
271
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value, children });
|
|
272
|
+
}
|
|
273
|
+
function useAuth() {
|
|
274
|
+
const ctx = react.useContext(AuthContext);
|
|
275
|
+
if (!ctx) {
|
|
276
|
+
throw new Error("useAuth must be used within an AuthProvider");
|
|
277
|
+
}
|
|
278
|
+
return ctx;
|
|
279
|
+
}
|
|
280
|
+
function ProtectedRoute({
|
|
281
|
+
client,
|
|
282
|
+
redirectTo,
|
|
283
|
+
children
|
|
284
|
+
}) {
|
|
285
|
+
const location = reactRouterDom.useLocation();
|
|
286
|
+
const loginPath = redirectTo ?? client.options.loginPath;
|
|
287
|
+
if (!client.isLoggedIn()) {
|
|
288
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: loginPath, replace: true, state: { from: location } });
|
|
289
|
+
}
|
|
290
|
+
return children ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Outlet, {});
|
|
291
|
+
}
|
|
292
|
+
function GuestRoute({
|
|
293
|
+
client,
|
|
294
|
+
redirectTo = "/",
|
|
295
|
+
children
|
|
296
|
+
}) {
|
|
297
|
+
if (client.isLoggedIn()) {
|
|
298
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: redirectTo, replace: true });
|
|
299
|
+
}
|
|
300
|
+
return children ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Outlet, {});
|
|
301
|
+
}
|
|
302
|
+
function useAutoLogout({
|
|
303
|
+
client,
|
|
304
|
+
intervalMs = 3e4,
|
|
305
|
+
enabled = true
|
|
306
|
+
}) {
|
|
307
|
+
const loggingOut = react.useRef(false);
|
|
308
|
+
react.useEffect(() => {
|
|
309
|
+
if (!enabled) return;
|
|
310
|
+
const tick = async () => {
|
|
311
|
+
if (!client.isLoggedIn() || loggingOut.current) return;
|
|
312
|
+
const expiry = client.getTokenExpiry();
|
|
313
|
+
if (expiry == null) return;
|
|
314
|
+
const expiryMs = expiry < 1e12 ? expiry * 1e3 : expiry;
|
|
315
|
+
if (Date.now() >= expiryMs) {
|
|
316
|
+
loggingOut.current = true;
|
|
317
|
+
try {
|
|
318
|
+
await client.logout();
|
|
319
|
+
} finally {
|
|
320
|
+
loggingOut.current = false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
void tick();
|
|
325
|
+
const id = window.setInterval(tick, intervalMs);
|
|
326
|
+
return () => window.clearInterval(id);
|
|
327
|
+
}, [client, intervalMs, enabled]);
|
|
328
|
+
}
|
|
329
|
+
var headerStyle = {
|
|
330
|
+
display: "flex",
|
|
331
|
+
flexDirection: "column",
|
|
332
|
+
alignItems: "center",
|
|
333
|
+
gap: 4,
|
|
334
|
+
marginBottom: 24
|
|
335
|
+
};
|
|
336
|
+
var titleStyle = {
|
|
337
|
+
margin: 0,
|
|
338
|
+
textAlign: "center",
|
|
339
|
+
fontWeight: 600
|
|
340
|
+
};
|
|
341
|
+
var descriptionStyle = {
|
|
342
|
+
margin: 0,
|
|
343
|
+
textAlign: "center"
|
|
344
|
+
};
|
|
345
|
+
var inputStyle = {
|
|
346
|
+
height: 48,
|
|
347
|
+
borderRadius: 6
|
|
348
|
+
};
|
|
349
|
+
var formGapStyle = {
|
|
350
|
+
display: "flex",
|
|
351
|
+
flexDirection: "column",
|
|
352
|
+
gap: 16
|
|
353
|
+
};
|
|
354
|
+
function defaultPlaceholder(mode) {
|
|
355
|
+
if (mode === "mobile") return "Enter Your Mobile Number";
|
|
356
|
+
if (mode === "email") return "Enter Your Email";
|
|
357
|
+
return "Enter Your Email / Phone";
|
|
358
|
+
}
|
|
359
|
+
function validateIdentifier(mode, value) {
|
|
360
|
+
const trimmed = value.trim();
|
|
361
|
+
if (!trimmed) return Promise.reject(new Error("Required"));
|
|
362
|
+
if (mode === "mobile") {
|
|
363
|
+
if (!/^\d{10}$/.test(trimmed)) {
|
|
364
|
+
return Promise.reject(new Error("Enter a valid 10-digit mobile number"));
|
|
365
|
+
}
|
|
366
|
+
} else if (mode === "email") {
|
|
367
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
|
|
368
|
+
return Promise.reject(new Error("Enter a valid email"));
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
const isMobile = /^\d{10}$/.test(trimmed);
|
|
372
|
+
const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed);
|
|
373
|
+
if (!isMobile && !isEmail) {
|
|
374
|
+
return Promise.reject(new Error("Enter a valid email or 10-digit mobile"));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return Promise.resolve();
|
|
378
|
+
}
|
|
379
|
+
function IdentifierForm({
|
|
380
|
+
onSubmit,
|
|
381
|
+
loading = false,
|
|
382
|
+
mode = "either",
|
|
383
|
+
submitLabel = "Continue",
|
|
384
|
+
title = "Sign in to Indus Action",
|
|
385
|
+
description = "Please Enter Your Email / Phone",
|
|
386
|
+
placeholder,
|
|
387
|
+
initialIdentifier = "",
|
|
388
|
+
label = null
|
|
389
|
+
}) {
|
|
390
|
+
const [form] = antd.Form.useForm();
|
|
391
|
+
const inputPlaceholder = placeholder ?? defaultPlaceholder(mode);
|
|
392
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: formGapStyle, children: [
|
|
393
|
+
title || description ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
|
|
394
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(antd.Typography.Title, { level: 3, style: titleStyle, children: title }) : null,
|
|
395
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(antd.Typography.Paragraph, { type: "secondary", style: descriptionStyle, children: description }) : null
|
|
396
|
+
] }) : null,
|
|
397
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
398
|
+
antd.Form,
|
|
399
|
+
{
|
|
400
|
+
form,
|
|
401
|
+
layout: "vertical",
|
|
402
|
+
initialValues: { identifier: initialIdentifier },
|
|
403
|
+
onFinish: onSubmit,
|
|
404
|
+
requiredMark: false,
|
|
405
|
+
style: { width: "100%" },
|
|
406
|
+
children: [
|
|
407
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
408
|
+
antd.Form.Item,
|
|
409
|
+
{
|
|
410
|
+
name: "identifier",
|
|
411
|
+
label: label ?? void 0,
|
|
412
|
+
rules: [
|
|
413
|
+
{
|
|
414
|
+
validator: async (_, value) => validateIdentifier(mode, value ?? "")
|
|
415
|
+
}
|
|
416
|
+
],
|
|
417
|
+
style: { marginBottom: 16 },
|
|
418
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
419
|
+
antd.Input,
|
|
420
|
+
{
|
|
421
|
+
size: "large",
|
|
422
|
+
placeholder: inputPlaceholder,
|
|
423
|
+
autoComplete: mode === "mobile" ? "tel" : "username",
|
|
424
|
+
inputMode: mode === "mobile" ? "numeric" : "text",
|
|
425
|
+
style: inputStyle
|
|
426
|
+
}
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
),
|
|
430
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
431
|
+
antd.Button,
|
|
432
|
+
{
|
|
433
|
+
type: "primary",
|
|
434
|
+
htmlType: "submit",
|
|
435
|
+
loading,
|
|
436
|
+
block: true,
|
|
437
|
+
size: "large",
|
|
438
|
+
style: { height: 48, borderRadius: 6 },
|
|
439
|
+
children: submitLabel
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
)
|
|
445
|
+
] });
|
|
446
|
+
}
|
|
447
|
+
function OtpForm({
|
|
448
|
+
onSubmit,
|
|
449
|
+
onResend,
|
|
450
|
+
loading = false,
|
|
451
|
+
resendLoading = false,
|
|
452
|
+
length = 6,
|
|
453
|
+
title = "Check your email / phone for a code",
|
|
454
|
+
description = "Please enter OTP to continue to your account.",
|
|
455
|
+
submitLabel = "Continue",
|
|
456
|
+
label = null,
|
|
457
|
+
resendSeconds = 120
|
|
458
|
+
}) {
|
|
459
|
+
const [form] = antd.Form.useForm();
|
|
460
|
+
const [secondsLeft, setSecondsLeft] = react.useState(resendSeconds);
|
|
461
|
+
react.useEffect(() => {
|
|
462
|
+
if (secondsLeft <= 0) return;
|
|
463
|
+
const id = window.setTimeout(() => setSecondsLeft((s) => s - 1), 1e3);
|
|
464
|
+
return () => window.clearTimeout(id);
|
|
465
|
+
}, [secondsLeft]);
|
|
466
|
+
const handleResend = async () => {
|
|
467
|
+
if (!onResend || secondsLeft > 0) return;
|
|
468
|
+
await onResend();
|
|
469
|
+
setSecondsLeft(resendSeconds);
|
|
470
|
+
};
|
|
471
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: formGapStyle, children: [
|
|
472
|
+
title || description ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
|
|
473
|
+
title ? /* @__PURE__ */ jsxRuntime.jsx(antd.Typography.Title, { level: 3, style: titleStyle, children: title }) : null,
|
|
474
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(antd.Typography.Paragraph, { type: "secondary", style: descriptionStyle, children: description }) : null
|
|
475
|
+
] }) : null,
|
|
476
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
477
|
+
antd.Form,
|
|
478
|
+
{
|
|
479
|
+
form,
|
|
480
|
+
layout: "vertical",
|
|
481
|
+
onFinish: onSubmit,
|
|
482
|
+
requiredMark: false,
|
|
483
|
+
style: { width: "100%" },
|
|
484
|
+
children: [
|
|
485
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
486
|
+
antd.Form.Item,
|
|
487
|
+
{
|
|
488
|
+
name: "otp",
|
|
489
|
+
label: label ?? void 0,
|
|
490
|
+
rules: [
|
|
491
|
+
{ required: true, message: "Enter OTP" },
|
|
492
|
+
{
|
|
493
|
+
len: length,
|
|
494
|
+
message: `OTP must be ${length} digits`
|
|
495
|
+
}
|
|
496
|
+
],
|
|
497
|
+
style: { marginBottom: 16 },
|
|
498
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
499
|
+
antd.Input.OTP,
|
|
500
|
+
{
|
|
501
|
+
length,
|
|
502
|
+
size: "large",
|
|
503
|
+
style: { display: "flex", width: "100%" }
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
}
|
|
507
|
+
),
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
509
|
+
antd.Button,
|
|
510
|
+
{
|
|
511
|
+
type: "primary",
|
|
512
|
+
htmlType: "submit",
|
|
513
|
+
loading,
|
|
514
|
+
block: true,
|
|
515
|
+
size: "large",
|
|
516
|
+
style: { height: 48, borderRadius: 6 },
|
|
517
|
+
children: submitLabel
|
|
518
|
+
}
|
|
519
|
+
)
|
|
520
|
+
]
|
|
521
|
+
}
|
|
522
|
+
),
|
|
523
|
+
onResend ? /* @__PURE__ */ jsxRuntime.jsx(antd.Typography.Paragraph, { style: { marginTop: 8, textAlign: "center" }, children: secondsLeft > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(antd.Typography.Text, { type: "secondary", children: [
|
|
524
|
+
"Resend OTP in ",
|
|
525
|
+
secondsLeft,
|
|
526
|
+
"s"
|
|
527
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
528
|
+
antd.Button,
|
|
529
|
+
{
|
|
530
|
+
type: "link",
|
|
531
|
+
loading: resendLoading,
|
|
532
|
+
onClick: () => void handleResend(),
|
|
533
|
+
children: "Resend OTP"
|
|
534
|
+
}
|
|
535
|
+
) }) : null
|
|
536
|
+
] });
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
exports.AuthProvider = AuthProvider;
|
|
540
|
+
exports.DEFAULT_AUTH_PATHS = DEFAULT_AUTH_PATHS;
|
|
541
|
+
exports.GuestRoute = GuestRoute;
|
|
542
|
+
exports.IdentifierForm = IdentifierForm;
|
|
543
|
+
exports.MOBILE_SEND_OTP_PATH = MOBILE_SEND_OTP_PATH;
|
|
544
|
+
exports.OtpForm = OtpForm;
|
|
545
|
+
exports.ProtectedRoute = ProtectedRoute;
|
|
546
|
+
exports.authCookieKeyList = authCookieKeyList;
|
|
547
|
+
exports.buildCookieKeys = buildCookieKeys;
|
|
548
|
+
exports.clearAuthStorage = clearAuthStorage;
|
|
549
|
+
exports.createAuthClient = createAuthClient;
|
|
550
|
+
exports.getSessionProfile = getSessionProfile;
|
|
551
|
+
exports.getTokenExpiry = getTokenExpiry;
|
|
552
|
+
exports.isLoggedIn = isLoggedIn;
|
|
553
|
+
exports.joinPath = joinPath;
|
|
554
|
+
exports.persistSessionProfile = persistSessionProfile;
|
|
555
|
+
exports.useAuth = useAuth;
|
|
556
|
+
exports.useAutoLogout = useAutoLogout;
|
|
557
|
+
//# sourceMappingURL=index.cjs.map
|
|
558
|
+
//# sourceMappingURL=index.cjs.map
|