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