@rqdhw3n/react-auth-flow 1.0.1 → 1.0.3
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.cjs.js +944 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +293 -0
- package/dist/index.es.js +944 -0
- package/dist/index.es.js.map +1 -0
- package/package.json +13 -10
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const reactRouterDom = require("react-router-dom");
|
|
6
|
+
const AuthContext = React.createContext(
|
|
7
|
+
void 0
|
|
8
|
+
);
|
|
9
|
+
AuthContext.displayName = "AuthContext";
|
|
10
|
+
function normalizeError(error) {
|
|
11
|
+
if (isAuthError(error)) {
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
if (error instanceof Error) {
|
|
15
|
+
return {
|
|
16
|
+
code: "ERROR",
|
|
17
|
+
message: error.message,
|
|
18
|
+
statusCode: void 0
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (typeof error === "object" && error !== null && "status" in error && "statusText" in error) {
|
|
22
|
+
const fetchError = error;
|
|
23
|
+
return {
|
|
24
|
+
code: `HTTP_${fetchError.status}`,
|
|
25
|
+
message: fetchError.statusText || "Request failed",
|
|
26
|
+
statusCode: fetchError.status
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (typeof error === "object" && error !== null && "error" in error && typeof error.error === "object") {
|
|
30
|
+
const apiError = error.error;
|
|
31
|
+
return {
|
|
32
|
+
code: apiError.code || "API_ERROR",
|
|
33
|
+
message: apiError.message || "An error occurred",
|
|
34
|
+
statusCode: apiError.statusCode || void 0,
|
|
35
|
+
details: apiError.details
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
39
|
+
const objError = error;
|
|
40
|
+
return {
|
|
41
|
+
code: objError.code || "ERROR",
|
|
42
|
+
message: typeof objError.message === "string" ? objError.message : "An unknown error occurred"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (typeof error === "string") {
|
|
46
|
+
return {
|
|
47
|
+
code: "ERROR",
|
|
48
|
+
message: error
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
code: "UNKNOWN_ERROR",
|
|
53
|
+
message: "An unknown error occurred"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function isAuthError(error) {
|
|
57
|
+
return typeof error === "object" && error !== null && "code" in error && "message" in error && typeof error.code === "string" && typeof error.message === "string";
|
|
58
|
+
}
|
|
59
|
+
const DEFAULT_ENDPOINTS = {
|
|
60
|
+
login: "/auth/login",
|
|
61
|
+
register: "/auth/register",
|
|
62
|
+
logout: "/auth/logout",
|
|
63
|
+
me: "/auth/me",
|
|
64
|
+
refresh: "/auth/refresh",
|
|
65
|
+
forgotPassword: "/auth/forgot-password",
|
|
66
|
+
resetPassword: "/auth/reset-password",
|
|
67
|
+
verifyEmail: "/auth/verify-email"
|
|
68
|
+
};
|
|
69
|
+
const defaultAdapter = async (url, options) => {
|
|
70
|
+
const response = await fetch(url, options);
|
|
71
|
+
return response;
|
|
72
|
+
};
|
|
73
|
+
function createAuthClient(config = {}) {
|
|
74
|
+
const {
|
|
75
|
+
baseURL = "",
|
|
76
|
+
endpoints = {},
|
|
77
|
+
headers = {},
|
|
78
|
+
credentials = "include",
|
|
79
|
+
adapter = defaultAdapter
|
|
80
|
+
} = config;
|
|
81
|
+
const finalEndpoints = { ...DEFAULT_ENDPOINTS, ...endpoints };
|
|
82
|
+
async function request(method, endpoint, data) {
|
|
83
|
+
const url = `${baseURL}${endpoint}`;
|
|
84
|
+
const options = {
|
|
85
|
+
method,
|
|
86
|
+
credentials,
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
...headers
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (data) {
|
|
93
|
+
options.body = JSON.stringify(data);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const response = await adapter(url, options);
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
let errorData = null;
|
|
99
|
+
try {
|
|
100
|
+
errorData = await response.json();
|
|
101
|
+
} catch {
|
|
102
|
+
errorData = {
|
|
103
|
+
error: {
|
|
104
|
+
message: response.statusText,
|
|
105
|
+
statusCode: response.status
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
throw errorData;
|
|
110
|
+
}
|
|
111
|
+
const contentType = response.headers.get("content-type");
|
|
112
|
+
if (contentType && contentType.includes("application/json")) {
|
|
113
|
+
return await response.json();
|
|
114
|
+
}
|
|
115
|
+
return {};
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw normalizeError(error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
/**
|
|
122
|
+
* Login with email and password
|
|
123
|
+
*/
|
|
124
|
+
async login(email, password, rememberMe) {
|
|
125
|
+
const response = await request(
|
|
126
|
+
"POST",
|
|
127
|
+
finalEndpoints.login,
|
|
128
|
+
{ email, password, rememberMe }
|
|
129
|
+
);
|
|
130
|
+
return response;
|
|
131
|
+
},
|
|
132
|
+
/**
|
|
133
|
+
* Register a new account
|
|
134
|
+
*/
|
|
135
|
+
async register(payload) {
|
|
136
|
+
const response = await request(
|
|
137
|
+
"POST",
|
|
138
|
+
finalEndpoints.register,
|
|
139
|
+
payload
|
|
140
|
+
);
|
|
141
|
+
return response;
|
|
142
|
+
},
|
|
143
|
+
/**
|
|
144
|
+
* Logout the user
|
|
145
|
+
*/
|
|
146
|
+
async logout() {
|
|
147
|
+
await request("POST", finalEndpoints.logout);
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* Get current user
|
|
151
|
+
*/
|
|
152
|
+
async me() {
|
|
153
|
+
const response = await request(
|
|
154
|
+
"GET",
|
|
155
|
+
finalEndpoints.me
|
|
156
|
+
);
|
|
157
|
+
return response;
|
|
158
|
+
},
|
|
159
|
+
/**
|
|
160
|
+
* Refresh the authentication session
|
|
161
|
+
*/
|
|
162
|
+
async refresh() {
|
|
163
|
+
const response = await request(
|
|
164
|
+
"POST",
|
|
165
|
+
finalEndpoints.refresh
|
|
166
|
+
);
|
|
167
|
+
return response;
|
|
168
|
+
},
|
|
169
|
+
/**
|
|
170
|
+
* Request a password reset
|
|
171
|
+
*/
|
|
172
|
+
async forgotPassword(email) {
|
|
173
|
+
await request("POST", finalEndpoints.forgotPassword, { email });
|
|
174
|
+
},
|
|
175
|
+
/**
|
|
176
|
+
* Reset password with token
|
|
177
|
+
*/
|
|
178
|
+
async resetPassword(token, password, confirmPassword) {
|
|
179
|
+
await request("POST", finalEndpoints.resetPassword, {
|
|
180
|
+
token,
|
|
181
|
+
password,
|
|
182
|
+
confirmPassword
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
/**
|
|
186
|
+
* Verify email with token
|
|
187
|
+
*/
|
|
188
|
+
async verifyEmail(token, email) {
|
|
189
|
+
await request("POST", finalEndpoints.verifyEmail, { token, email });
|
|
190
|
+
},
|
|
191
|
+
/**
|
|
192
|
+
* Set custom headers for subsequent requests
|
|
193
|
+
*/
|
|
194
|
+
setHeaders(newHeaders) {
|
|
195
|
+
Object.assign(headers, newHeaders);
|
|
196
|
+
},
|
|
197
|
+
/**
|
|
198
|
+
* Get current endpoints configuration
|
|
199
|
+
*/
|
|
200
|
+
getEndpoints() {
|
|
201
|
+
return finalEndpoints;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const initialState = {
|
|
206
|
+
user: null,
|
|
207
|
+
isAuthenticated: false,
|
|
208
|
+
isLoading: false,
|
|
209
|
+
error: null
|
|
210
|
+
};
|
|
211
|
+
function authReducer(state, action) {
|
|
212
|
+
switch (action.type) {
|
|
213
|
+
case "SET_LOADING":
|
|
214
|
+
return { ...state, isLoading: action.payload };
|
|
215
|
+
case "SET_ERROR":
|
|
216
|
+
return { ...state, error: action.payload, isLoading: false };
|
|
217
|
+
case "SET_USER":
|
|
218
|
+
return {
|
|
219
|
+
...state,
|
|
220
|
+
user: action.payload,
|
|
221
|
+
isAuthenticated: action.payload !== null,
|
|
222
|
+
error: null,
|
|
223
|
+
isLoading: false
|
|
224
|
+
};
|
|
225
|
+
case "LOGOUT":
|
|
226
|
+
return initialState;
|
|
227
|
+
default:
|
|
228
|
+
return state;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const AuthProvider = ({
|
|
232
|
+
children,
|
|
233
|
+
baseURL = "",
|
|
234
|
+
endpoints = {},
|
|
235
|
+
onAuthError,
|
|
236
|
+
autoRefresh = true,
|
|
237
|
+
refreshInterval = 5 * 60 * 1e3
|
|
238
|
+
// 5 minutes
|
|
239
|
+
}) => {
|
|
240
|
+
const [state, dispatch] = React.useReducer(authReducer, initialState);
|
|
241
|
+
const clientRef = React.useRef(
|
|
242
|
+
createAuthClient({
|
|
243
|
+
baseURL,
|
|
244
|
+
endpoints,
|
|
245
|
+
credentials: "include"
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
const client = clientRef.current;
|
|
249
|
+
const handleError = React.useCallback(
|
|
250
|
+
(error) => {
|
|
251
|
+
dispatch({ type: "SET_ERROR", payload: error });
|
|
252
|
+
onAuthError?.(error);
|
|
253
|
+
},
|
|
254
|
+
[onAuthError]
|
|
255
|
+
);
|
|
256
|
+
const restoreSession = React.useCallback(async () => {
|
|
257
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
258
|
+
try {
|
|
259
|
+
const response = await client.me();
|
|
260
|
+
if (response.user) {
|
|
261
|
+
dispatch({ type: "SET_USER", payload: response.user });
|
|
262
|
+
} else {
|
|
263
|
+
dispatch({ type: "LOGOUT" });
|
|
264
|
+
}
|
|
265
|
+
} catch (_error) {
|
|
266
|
+
dispatch({ type: "SET_LOADING", payload: false });
|
|
267
|
+
}
|
|
268
|
+
}, [client]);
|
|
269
|
+
const refreshSession = React.useCallback(async () => {
|
|
270
|
+
try {
|
|
271
|
+
const response = await client.refresh();
|
|
272
|
+
if (response.user) {
|
|
273
|
+
dispatch({ type: "SET_USER", payload: response.user });
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
const authError = normalizeError(error);
|
|
277
|
+
handleError(authError);
|
|
278
|
+
}
|
|
279
|
+
}, [client, handleError]);
|
|
280
|
+
const login = React.useCallback(
|
|
281
|
+
async (payload) => {
|
|
282
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
283
|
+
try {
|
|
284
|
+
const response = await client.login(payload.email, payload.password);
|
|
285
|
+
if (!response.user) {
|
|
286
|
+
throw new Error("No user data in response");
|
|
287
|
+
}
|
|
288
|
+
dispatch({ type: "SET_USER", payload: response.user });
|
|
289
|
+
return response.user;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
const authError = normalizeError(error);
|
|
292
|
+
handleError(authError);
|
|
293
|
+
throw authError;
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
[client, handleError]
|
|
297
|
+
);
|
|
298
|
+
const register = React.useCallback(
|
|
299
|
+
async (payload) => {
|
|
300
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
301
|
+
try {
|
|
302
|
+
const response = await client.register(payload);
|
|
303
|
+
if (!response.user) {
|
|
304
|
+
throw new Error("No user data in response");
|
|
305
|
+
}
|
|
306
|
+
dispatch({ type: "SET_USER", payload: response.user });
|
|
307
|
+
return response.user;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
const authError = normalizeError(error);
|
|
310
|
+
handleError(authError);
|
|
311
|
+
throw authError;
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
[client, handleError]
|
|
315
|
+
);
|
|
316
|
+
const logout = React.useCallback(async () => {
|
|
317
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
318
|
+
try {
|
|
319
|
+
await client.logout();
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error("Logout error:", error);
|
|
322
|
+
} finally {
|
|
323
|
+
dispatch({ type: "LOGOUT" });
|
|
324
|
+
}
|
|
325
|
+
}, [client]);
|
|
326
|
+
const forgotPassword = React.useCallback(
|
|
327
|
+
async (payload) => {
|
|
328
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
329
|
+
try {
|
|
330
|
+
await client.forgotPassword(payload.email);
|
|
331
|
+
dispatch({ type: "SET_LOADING", payload: false });
|
|
332
|
+
} catch (error) {
|
|
333
|
+
const authError = normalizeError(error);
|
|
334
|
+
handleError(authError);
|
|
335
|
+
throw authError;
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
[client, handleError]
|
|
339
|
+
);
|
|
340
|
+
const resetPassword = React.useCallback(
|
|
341
|
+
async (payload) => {
|
|
342
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
343
|
+
try {
|
|
344
|
+
await client.resetPassword(
|
|
345
|
+
payload.token,
|
|
346
|
+
payload.password,
|
|
347
|
+
payload.confirmPassword
|
|
348
|
+
);
|
|
349
|
+
dispatch({ type: "SET_LOADING", payload: false });
|
|
350
|
+
} catch (error) {
|
|
351
|
+
const authError = normalizeError(error);
|
|
352
|
+
handleError(authError);
|
|
353
|
+
throw authError;
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
[client, handleError]
|
|
357
|
+
);
|
|
358
|
+
const verifyEmail = React.useCallback(
|
|
359
|
+
async (payload) => {
|
|
360
|
+
dispatch({ type: "SET_LOADING", payload: true });
|
|
361
|
+
try {
|
|
362
|
+
await client.verifyEmail(payload.token, payload.email);
|
|
363
|
+
dispatch({ type: "SET_LOADING", payload: false });
|
|
364
|
+
} catch (error) {
|
|
365
|
+
const authError = normalizeError(error);
|
|
366
|
+
handleError(authError);
|
|
367
|
+
throw authError;
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
[client, handleError]
|
|
371
|
+
);
|
|
372
|
+
const setUser = React.useCallback((user) => {
|
|
373
|
+
dispatch({ type: "SET_USER", payload: user });
|
|
374
|
+
}, []);
|
|
375
|
+
React.useEffect(() => {
|
|
376
|
+
restoreSession();
|
|
377
|
+
}, [restoreSession]);
|
|
378
|
+
React.useEffect(() => {
|
|
379
|
+
if (!autoRefresh || !state.isAuthenticated) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const interval = setInterval(() => {
|
|
383
|
+
refreshSession();
|
|
384
|
+
}, refreshInterval);
|
|
385
|
+
return () => clearInterval(interval);
|
|
386
|
+
}, [autoRefresh, state.isAuthenticated, refreshSession, refreshInterval]);
|
|
387
|
+
const value = {
|
|
388
|
+
...state,
|
|
389
|
+
login,
|
|
390
|
+
register,
|
|
391
|
+
logout,
|
|
392
|
+
forgotPassword,
|
|
393
|
+
resetPassword,
|
|
394
|
+
verifyEmail,
|
|
395
|
+
refreshSession,
|
|
396
|
+
restoreSession,
|
|
397
|
+
setUser
|
|
398
|
+
};
|
|
399
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value, children });
|
|
400
|
+
};
|
|
401
|
+
function useAuth() {
|
|
402
|
+
const context = React.useContext(AuthContext);
|
|
403
|
+
if (!context) {
|
|
404
|
+
throw new Error("useAuth must be used within an AuthProvider");
|
|
405
|
+
}
|
|
406
|
+
return context;
|
|
407
|
+
}
|
|
408
|
+
const LoginForm = ({
|
|
409
|
+
className = "",
|
|
410
|
+
labels = {},
|
|
411
|
+
placeholders = {},
|
|
412
|
+
submitButtonText = "Login",
|
|
413
|
+
onSuccess,
|
|
414
|
+
onError
|
|
415
|
+
}) => {
|
|
416
|
+
const { login, isLoading: authLoading, error: authError } = useAuth();
|
|
417
|
+
const [formError, setFormError] = React.useState("");
|
|
418
|
+
const [email, setEmail] = React.useState("");
|
|
419
|
+
const [password, setPassword] = React.useState("");
|
|
420
|
+
const [rememberMe, setRememberMe] = React.useState(false);
|
|
421
|
+
const isLoading = authLoading;
|
|
422
|
+
const error = formError || authError?.message;
|
|
423
|
+
const handleSubmit = async (e) => {
|
|
424
|
+
e.preventDefault();
|
|
425
|
+
setFormError("");
|
|
426
|
+
if (!email || !password) {
|
|
427
|
+
setFormError("Email and password are required");
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
const user = await login({ email, password, rememberMe });
|
|
432
|
+
onSuccess?.(user);
|
|
433
|
+
} catch (err) {
|
|
434
|
+
const errorMsg = err instanceof Error ? err.message : "Login failed";
|
|
435
|
+
setFormError(errorMsg);
|
|
436
|
+
onError?.({
|
|
437
|
+
code: "LOGIN_ERROR",
|
|
438
|
+
message: errorMsg
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className, children: [
|
|
443
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
444
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "email", className: "auth-form-label", children: labels.email || "Email" }),
|
|
445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
446
|
+
"input",
|
|
447
|
+
{
|
|
448
|
+
id: "email",
|
|
449
|
+
type: "email",
|
|
450
|
+
value: email,
|
|
451
|
+
onChange: (e) => setEmail(e.target.value),
|
|
452
|
+
placeholder: placeholders.email || "your@email.com",
|
|
453
|
+
disabled: isLoading,
|
|
454
|
+
className: "auth-form-input",
|
|
455
|
+
required: true
|
|
456
|
+
}
|
|
457
|
+
)
|
|
458
|
+
] }),
|
|
459
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "password", className: "auth-form-label", children: labels.password || "Password" }),
|
|
461
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
462
|
+
"input",
|
|
463
|
+
{
|
|
464
|
+
id: "password",
|
|
465
|
+
type: "password",
|
|
466
|
+
value: password,
|
|
467
|
+
onChange: (e) => setPassword(e.target.value),
|
|
468
|
+
placeholder: placeholders.password || "••••••••",
|
|
469
|
+
disabled: isLoading,
|
|
470
|
+
className: "auth-form-input",
|
|
471
|
+
required: true
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
] }),
|
|
475
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group auth-form-checkbox", children: [
|
|
476
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
477
|
+
"input",
|
|
478
|
+
{
|
|
479
|
+
id: "rememberMe",
|
|
480
|
+
type: "checkbox",
|
|
481
|
+
checked: rememberMe,
|
|
482
|
+
onChange: (e) => setRememberMe(e.target.checked),
|
|
483
|
+
disabled: isLoading,
|
|
484
|
+
className: "auth-form-input"
|
|
485
|
+
}
|
|
486
|
+
),
|
|
487
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "rememberMe", className: "auth-form-label", children: labels.rememberMe || "Remember me" })
|
|
488
|
+
] }),
|
|
489
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-form-error", children: error }),
|
|
490
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
491
|
+
"button",
|
|
492
|
+
{
|
|
493
|
+
type: "submit",
|
|
494
|
+
disabled: isLoading,
|
|
495
|
+
className: "auth-form-button auth-form-button-primary",
|
|
496
|
+
children: isLoading ? "Loading..." : submitButtonText
|
|
497
|
+
}
|
|
498
|
+
)
|
|
499
|
+
] });
|
|
500
|
+
};
|
|
501
|
+
LoginForm.displayName = "LoginForm";
|
|
502
|
+
const RegisterForm = ({
|
|
503
|
+
className = "",
|
|
504
|
+
labels = {},
|
|
505
|
+
placeholders = {},
|
|
506
|
+
submitButtonText = "Register",
|
|
507
|
+
onSuccess,
|
|
508
|
+
onError
|
|
509
|
+
}) => {
|
|
510
|
+
const { register, isLoading: authLoading, error: authError } = useAuth();
|
|
511
|
+
const [formError, setFormError] = React.useState("");
|
|
512
|
+
const [name, setName] = React.useState("");
|
|
513
|
+
const [email, setEmail] = React.useState("");
|
|
514
|
+
const [password, setPassword] = React.useState("");
|
|
515
|
+
const [confirmPassword, setConfirmPassword] = React.useState("");
|
|
516
|
+
const isLoading = authLoading;
|
|
517
|
+
const error = formError || authError?.message;
|
|
518
|
+
const handleSubmit = async (e) => {
|
|
519
|
+
e.preventDefault();
|
|
520
|
+
setFormError("");
|
|
521
|
+
if (!name || !email || !password || !confirmPassword) {
|
|
522
|
+
setFormError("All fields are required");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (password !== confirmPassword) {
|
|
526
|
+
setFormError("Passwords do not match");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (password.length < 8) {
|
|
530
|
+
setFormError("Password must be at least 8 characters");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
const user = await register({
|
|
535
|
+
name,
|
|
536
|
+
email,
|
|
537
|
+
password,
|
|
538
|
+
confirmPassword
|
|
539
|
+
});
|
|
540
|
+
onSuccess?.(user);
|
|
541
|
+
} catch (err) {
|
|
542
|
+
const errorMsg = err instanceof Error ? err.message : "Registration failed";
|
|
543
|
+
setFormError(errorMsg);
|
|
544
|
+
onError?.({
|
|
545
|
+
code: "REGISTER_ERROR",
|
|
546
|
+
message: errorMsg
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className, children: [
|
|
551
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
552
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "name", className: "auth-form-label", children: labels.name || "Full Name" }),
|
|
553
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
554
|
+
"input",
|
|
555
|
+
{
|
|
556
|
+
id: "name",
|
|
557
|
+
type: "text",
|
|
558
|
+
value: name,
|
|
559
|
+
onChange: (e) => setName(e.target.value),
|
|
560
|
+
placeholder: placeholders.name || "John Doe",
|
|
561
|
+
disabled: isLoading,
|
|
562
|
+
className: "auth-form-input",
|
|
563
|
+
required: true
|
|
564
|
+
}
|
|
565
|
+
)
|
|
566
|
+
] }),
|
|
567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "email", className: "auth-form-label", children: labels.email || "Email" }),
|
|
569
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
570
|
+
"input",
|
|
571
|
+
{
|
|
572
|
+
id: "email",
|
|
573
|
+
type: "email",
|
|
574
|
+
value: email,
|
|
575
|
+
onChange: (e) => setEmail(e.target.value),
|
|
576
|
+
placeholder: placeholders.email || "your@email.com",
|
|
577
|
+
disabled: isLoading,
|
|
578
|
+
className: "auth-form-input",
|
|
579
|
+
required: true
|
|
580
|
+
}
|
|
581
|
+
)
|
|
582
|
+
] }),
|
|
583
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "password", className: "auth-form-label", children: labels.password || "Password" }),
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
586
|
+
"input",
|
|
587
|
+
{
|
|
588
|
+
id: "password",
|
|
589
|
+
type: "password",
|
|
590
|
+
value: password,
|
|
591
|
+
onChange: (e) => setPassword(e.target.value),
|
|
592
|
+
placeholder: placeholders.password || "••••••••",
|
|
593
|
+
disabled: isLoading,
|
|
594
|
+
className: "auth-form-input",
|
|
595
|
+
required: true
|
|
596
|
+
}
|
|
597
|
+
)
|
|
598
|
+
] }),
|
|
599
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
600
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "confirmPassword", className: "auth-form-label", children: labels.confirmPassword || "Confirm Password" }),
|
|
601
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
602
|
+
"input",
|
|
603
|
+
{
|
|
604
|
+
id: "confirmPassword",
|
|
605
|
+
type: "password",
|
|
606
|
+
value: confirmPassword,
|
|
607
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
608
|
+
placeholder: placeholders.confirmPassword || "••••••••",
|
|
609
|
+
disabled: isLoading,
|
|
610
|
+
className: "auth-form-input",
|
|
611
|
+
required: true
|
|
612
|
+
}
|
|
613
|
+
)
|
|
614
|
+
] }),
|
|
615
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-form-error", children: error }),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
617
|
+
"button",
|
|
618
|
+
{
|
|
619
|
+
type: "submit",
|
|
620
|
+
disabled: isLoading,
|
|
621
|
+
className: "auth-form-button auth-form-button-primary",
|
|
622
|
+
children: isLoading ? "Loading..." : submitButtonText
|
|
623
|
+
}
|
|
624
|
+
)
|
|
625
|
+
] });
|
|
626
|
+
};
|
|
627
|
+
RegisterForm.displayName = "RegisterForm";
|
|
628
|
+
const ForgotPasswordForm = ({
|
|
629
|
+
className = "",
|
|
630
|
+
labels = {},
|
|
631
|
+
placeholders = {},
|
|
632
|
+
submitButtonText = "Send Reset Link",
|
|
633
|
+
onSuccess,
|
|
634
|
+
onError
|
|
635
|
+
}) => {
|
|
636
|
+
const {
|
|
637
|
+
forgotPassword,
|
|
638
|
+
isLoading: authLoading,
|
|
639
|
+
error: authError
|
|
640
|
+
} = useAuth();
|
|
641
|
+
const [formError, setFormError] = React.useState("");
|
|
642
|
+
const [email, setEmail] = React.useState("");
|
|
643
|
+
const [successMessage, setSuccessMessage] = React.useState("");
|
|
644
|
+
const isLoading = authLoading;
|
|
645
|
+
const error = formError || authError?.message;
|
|
646
|
+
const handleSubmit = async (e) => {
|
|
647
|
+
e.preventDefault();
|
|
648
|
+
setFormError("");
|
|
649
|
+
setSuccessMessage("");
|
|
650
|
+
if (!email) {
|
|
651
|
+
setFormError("Email is required");
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
try {
|
|
655
|
+
await forgotPassword({ email });
|
|
656
|
+
setSuccessMessage("Password reset link has been sent to your email");
|
|
657
|
+
setEmail("");
|
|
658
|
+
onSuccess?.();
|
|
659
|
+
} catch (err) {
|
|
660
|
+
const errorMsg = err instanceof Error ? err.message : "Request failed";
|
|
661
|
+
setFormError(errorMsg);
|
|
662
|
+
onError?.({
|
|
663
|
+
code: "FORGOT_PASSWORD_ERROR",
|
|
664
|
+
message: errorMsg
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
if (successMessage) {
|
|
669
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `auth-form-success ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "auth-form-success-message", children: successMessage }) });
|
|
670
|
+
}
|
|
671
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className, children: [
|
|
672
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
673
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "email", className: "auth-form-label", children: labels.email || "Email" }),
|
|
674
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
675
|
+
"input",
|
|
676
|
+
{
|
|
677
|
+
id: "email",
|
|
678
|
+
type: "email",
|
|
679
|
+
value: email,
|
|
680
|
+
onChange: (e) => setEmail(e.target.value),
|
|
681
|
+
placeholder: placeholders.email || "your@email.com",
|
|
682
|
+
disabled: isLoading,
|
|
683
|
+
className: "auth-form-input",
|
|
684
|
+
required: true
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
] }),
|
|
688
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-form-error", children: error }),
|
|
689
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
690
|
+
"button",
|
|
691
|
+
{
|
|
692
|
+
type: "submit",
|
|
693
|
+
disabled: isLoading,
|
|
694
|
+
className: "auth-form-button auth-form-button-primary",
|
|
695
|
+
children: isLoading ? "Loading..." : submitButtonText
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
] });
|
|
699
|
+
};
|
|
700
|
+
ForgotPasswordForm.displayName = "ForgotPasswordForm";
|
|
701
|
+
const ResetPasswordForm = ({
|
|
702
|
+
token,
|
|
703
|
+
className = "",
|
|
704
|
+
labels = {},
|
|
705
|
+
placeholders = {},
|
|
706
|
+
submitButtonText = "Reset Password",
|
|
707
|
+
onSuccess,
|
|
708
|
+
onError
|
|
709
|
+
}) => {
|
|
710
|
+
const {
|
|
711
|
+
resetPassword,
|
|
712
|
+
isLoading: authLoading,
|
|
713
|
+
error: authError
|
|
714
|
+
} = useAuth();
|
|
715
|
+
const [formError, setFormError] = React.useState("");
|
|
716
|
+
const [password, setPassword] = React.useState("");
|
|
717
|
+
const [confirmPassword, setConfirmPassword] = React.useState("");
|
|
718
|
+
const [successMessage, setSuccessMessage] = React.useState("");
|
|
719
|
+
const isLoading = authLoading;
|
|
720
|
+
const error = formError || authError?.message;
|
|
721
|
+
const handleSubmit = async (e) => {
|
|
722
|
+
e.preventDefault();
|
|
723
|
+
setFormError("");
|
|
724
|
+
setSuccessMessage("");
|
|
725
|
+
if (!password || !confirmPassword) {
|
|
726
|
+
setFormError("Both password fields are required");
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (password !== confirmPassword) {
|
|
730
|
+
setFormError("Passwords do not match");
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (password.length < 8) {
|
|
734
|
+
setFormError("Password must be at least 8 characters");
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
try {
|
|
738
|
+
await resetPassword({ token, password, confirmPassword });
|
|
739
|
+
setSuccessMessage("Password has been reset successfully");
|
|
740
|
+
setPassword("");
|
|
741
|
+
setConfirmPassword("");
|
|
742
|
+
onSuccess?.();
|
|
743
|
+
} catch (err) {
|
|
744
|
+
const errorMsg = err instanceof Error ? err.message : "Reset failed";
|
|
745
|
+
setFormError(errorMsg);
|
|
746
|
+
onError?.({
|
|
747
|
+
code: "RESET_PASSWORD_ERROR",
|
|
748
|
+
message: errorMsg
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
if (successMessage) {
|
|
753
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `auth-form-success ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "auth-form-success-message", children: successMessage }) });
|
|
754
|
+
}
|
|
755
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className, children: [
|
|
756
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
757
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "password", className: "auth-form-label", children: labels.password || "New Password" }),
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
759
|
+
"input",
|
|
760
|
+
{
|
|
761
|
+
id: "password",
|
|
762
|
+
type: "password",
|
|
763
|
+
value: password,
|
|
764
|
+
onChange: (e) => setPassword(e.target.value),
|
|
765
|
+
placeholder: placeholders.password || "••••••••",
|
|
766
|
+
disabled: isLoading,
|
|
767
|
+
className: "auth-form-input",
|
|
768
|
+
required: true
|
|
769
|
+
}
|
|
770
|
+
)
|
|
771
|
+
] }),
|
|
772
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
773
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "confirmPassword", className: "auth-form-label", children: labels.confirmPassword || "Confirm Password" }),
|
|
774
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
775
|
+
"input",
|
|
776
|
+
{
|
|
777
|
+
id: "confirmPassword",
|
|
778
|
+
type: "password",
|
|
779
|
+
value: confirmPassword,
|
|
780
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
781
|
+
placeholder: placeholders.confirmPassword || "••••••••",
|
|
782
|
+
disabled: isLoading,
|
|
783
|
+
className: "auth-form-input",
|
|
784
|
+
required: true
|
|
785
|
+
}
|
|
786
|
+
)
|
|
787
|
+
] }),
|
|
788
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-form-error", children: error }),
|
|
789
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
790
|
+
"button",
|
|
791
|
+
{
|
|
792
|
+
type: "submit",
|
|
793
|
+
disabled: isLoading,
|
|
794
|
+
className: "auth-form-button auth-form-button-primary",
|
|
795
|
+
children: isLoading ? "Loading..." : submitButtonText
|
|
796
|
+
}
|
|
797
|
+
)
|
|
798
|
+
] });
|
|
799
|
+
};
|
|
800
|
+
ResetPasswordForm.displayName = "ResetPasswordForm";
|
|
801
|
+
const VerifyEmailForm = ({
|
|
802
|
+
token: initialToken,
|
|
803
|
+
email: initialEmail,
|
|
804
|
+
className = "",
|
|
805
|
+
labels = {},
|
|
806
|
+
placeholders = {},
|
|
807
|
+
submitButtonText = "Verify Email",
|
|
808
|
+
onSuccess,
|
|
809
|
+
onError
|
|
810
|
+
}) => {
|
|
811
|
+
const {
|
|
812
|
+
verifyEmail,
|
|
813
|
+
isLoading: authLoading,
|
|
814
|
+
error: authError
|
|
815
|
+
} = useAuth();
|
|
816
|
+
const [formError, setFormError] = React.useState("");
|
|
817
|
+
const [email, setEmail] = React.useState(initialEmail || "");
|
|
818
|
+
const [token, setToken] = React.useState(initialToken || "");
|
|
819
|
+
const [successMessage, setSuccessMessage] = React.useState("");
|
|
820
|
+
const isLoading = authLoading;
|
|
821
|
+
const error = formError || authError?.message;
|
|
822
|
+
React.useEffect(() => {
|
|
823
|
+
if (token && email && !successMessage && !error) {
|
|
824
|
+
handleSubmit({ preventDefault: () => {
|
|
825
|
+
} });
|
|
826
|
+
}
|
|
827
|
+
}, [token, email]);
|
|
828
|
+
const handleSubmit = async (e) => {
|
|
829
|
+
e.preventDefault();
|
|
830
|
+
setFormError("");
|
|
831
|
+
setSuccessMessage("");
|
|
832
|
+
if (!token) {
|
|
833
|
+
setFormError("Verification token is required");
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
if (!email) {
|
|
837
|
+
setFormError("Email is required");
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
try {
|
|
841
|
+
await verifyEmail({ token, email });
|
|
842
|
+
setSuccessMessage("Email has been verified successfully");
|
|
843
|
+
onSuccess?.();
|
|
844
|
+
} catch (err) {
|
|
845
|
+
const errorMsg = err instanceof Error ? err.message : "Verification failed";
|
|
846
|
+
setFormError(errorMsg);
|
|
847
|
+
onError?.({
|
|
848
|
+
code: "VERIFY_EMAIL_ERROR",
|
|
849
|
+
message: errorMsg
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
if (successMessage) {
|
|
854
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `auth-form-success ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "auth-form-success-message", children: successMessage }) });
|
|
855
|
+
}
|
|
856
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className, children: [
|
|
857
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
858
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "email", className: "auth-form-label", children: labels.email || "Email" }),
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
860
|
+
"input",
|
|
861
|
+
{
|
|
862
|
+
id: "email",
|
|
863
|
+
type: "email",
|
|
864
|
+
value: email,
|
|
865
|
+
onChange: (e) => setEmail(e.target.value),
|
|
866
|
+
placeholder: placeholders.email || "your@email.com",
|
|
867
|
+
disabled: isLoading,
|
|
868
|
+
className: "auth-form-input",
|
|
869
|
+
required: true
|
|
870
|
+
}
|
|
871
|
+
)
|
|
872
|
+
] }),
|
|
873
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "auth-form-group", children: [
|
|
874
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "token", className: "auth-form-label", children: labels.token || "Verification Code" }),
|
|
875
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
876
|
+
"input",
|
|
877
|
+
{
|
|
878
|
+
id: "token",
|
|
879
|
+
type: "text",
|
|
880
|
+
value: token,
|
|
881
|
+
onChange: (e) => setToken(e.target.value),
|
|
882
|
+
placeholder: placeholders.token || "Enter verification code",
|
|
883
|
+
disabled: isLoading,
|
|
884
|
+
className: "auth-form-input",
|
|
885
|
+
required: true
|
|
886
|
+
}
|
|
887
|
+
)
|
|
888
|
+
] }),
|
|
889
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-form-error", children: error }),
|
|
890
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
891
|
+
"button",
|
|
892
|
+
{
|
|
893
|
+
type: "submit",
|
|
894
|
+
disabled: isLoading,
|
|
895
|
+
className: "auth-form-button auth-form-button-primary",
|
|
896
|
+
children: isLoading ? "Loading..." : submitButtonText
|
|
897
|
+
}
|
|
898
|
+
)
|
|
899
|
+
] });
|
|
900
|
+
};
|
|
901
|
+
VerifyEmailForm.displayName = "VerifyEmailForm";
|
|
902
|
+
const ProtectedRoute = ({
|
|
903
|
+
children,
|
|
904
|
+
redirectTo = "/login",
|
|
905
|
+
fallback,
|
|
906
|
+
roles,
|
|
907
|
+
permissions
|
|
908
|
+
}) => {
|
|
909
|
+
const { isAuthenticated, isLoading, user } = useAuth();
|
|
910
|
+
if (isLoading) {
|
|
911
|
+
return fallback || /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-loading", children: "Loading..." });
|
|
912
|
+
}
|
|
913
|
+
if (!isAuthenticated) {
|
|
914
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: redirectTo, replace: true });
|
|
915
|
+
}
|
|
916
|
+
if (roles && roles.length > 0) {
|
|
917
|
+
const hasRole = user?.roles?.some((role) => roles.includes(role));
|
|
918
|
+
if (!hasRole) {
|
|
919
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-forbidden", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "You do not have permission to access this page." }) });
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
if (permissions && permissions.length > 0) {
|
|
923
|
+
const hasPermission = user?.permissions?.some(
|
|
924
|
+
(permission) => permissions.includes(permission)
|
|
925
|
+
);
|
|
926
|
+
if (!hasPermission) {
|
|
927
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "auth-forbidden", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "You do not have permission to access this page." }) });
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
931
|
+
};
|
|
932
|
+
ProtectedRoute.displayName = "ProtectedRoute";
|
|
933
|
+
exports.AuthContext = AuthContext;
|
|
934
|
+
exports.AuthProvider = AuthProvider;
|
|
935
|
+
exports.ForgotPasswordForm = ForgotPasswordForm;
|
|
936
|
+
exports.LoginForm = LoginForm;
|
|
937
|
+
exports.ProtectedRoute = ProtectedRoute;
|
|
938
|
+
exports.RegisterForm = RegisterForm;
|
|
939
|
+
exports.ResetPasswordForm = ResetPasswordForm;
|
|
940
|
+
exports.VerifyEmailForm = VerifyEmailForm;
|
|
941
|
+
exports.createAuthClient = createAuthClient;
|
|
942
|
+
exports.normalizeError = normalizeError;
|
|
943
|
+
exports.useAuth = useAuth;
|
|
944
|
+
//# sourceMappingURL=index.cjs.js.map
|