@kendevelops/auth-flow-kit 1.3.2 → 1.3.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/README.md +239 -239
- package/dist/index.cjs +222 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +222 -25
- package/dist/index.js.map +1 -1
- package/package.json +45 -45
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,9 @@ var import_react = require("react");
|
|
|
34
34
|
|
|
35
35
|
// src/http.ts
|
|
36
36
|
function makeURL(baseURL, path) {
|
|
37
|
-
|
|
37
|
+
const base = baseURL.replace(/\/$/, "");
|
|
38
|
+
const suffix = path.startsWith("/") ? path : `/${path}`;
|
|
39
|
+
return `${base}${suffix}`;
|
|
38
40
|
}
|
|
39
41
|
function getStoredAccessToken() {
|
|
40
42
|
try {
|
|
@@ -45,8 +47,11 @@ function getStoredAccessToken() {
|
|
|
45
47
|
}
|
|
46
48
|
function setStoredAccessToken(token) {
|
|
47
49
|
try {
|
|
48
|
-
if (token)
|
|
49
|
-
|
|
50
|
+
if (token) {
|
|
51
|
+
localStorage.setItem("afk_access_token", token);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
localStorage.removeItem("afk_access_token");
|
|
50
55
|
} catch {
|
|
51
56
|
}
|
|
52
57
|
}
|
|
@@ -55,20 +60,27 @@ async function httpJSON(url, opts = {}, withAuth = false) {
|
|
|
55
60
|
"Content-Type": "application/json"
|
|
56
61
|
};
|
|
57
62
|
if (withAuth) {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
63
|
+
const storedToken = getStoredAccessToken();
|
|
64
|
+
if (storedToken) {
|
|
65
|
+
headers["Authorization"] = `Bearer ${storedToken}`;
|
|
66
|
+
}
|
|
60
67
|
}
|
|
61
68
|
const res = await fetch(url, {
|
|
62
69
|
...opts,
|
|
63
|
-
headers: {
|
|
70
|
+
headers: {
|
|
71
|
+
...headers,
|
|
72
|
+
...opts.headers || {}
|
|
73
|
+
}
|
|
64
74
|
});
|
|
65
75
|
if (!res.ok) {
|
|
66
76
|
let message = `Request failed (${res.status})`;
|
|
67
77
|
const contentType = res.headers.get("content-type") || "";
|
|
68
78
|
if (contentType.includes("application/json")) {
|
|
69
79
|
try {
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
80
|
+
const body = await res.json();
|
|
81
|
+
if (body?.message) {
|
|
82
|
+
message = body.message;
|
|
83
|
+
}
|
|
72
84
|
} catch {
|
|
73
85
|
}
|
|
74
86
|
}
|
|
@@ -188,18 +200,29 @@ function PasswordResetScreen() {
|
|
|
188
200
|
const [email, setEmail] = (0, import_react3.useState)("");
|
|
189
201
|
const [sent, setSent] = (0, import_react3.useState)(false);
|
|
190
202
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
203
|
+
const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
191
204
|
const requestReset = async (e) => {
|
|
192
205
|
e.preventDefault();
|
|
193
206
|
setError(null);
|
|
207
|
+
const trimmedEmail = email.trim();
|
|
208
|
+
if (!trimmedEmail) {
|
|
209
|
+
setError("Email is required.");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (!isValidEmail(trimmedEmail)) {
|
|
213
|
+
setError("Enter a valid email address.");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
194
216
|
try {
|
|
195
217
|
const url = makeURL(config.baseURL, config.endpoints.forgot);
|
|
196
218
|
await httpJSON(url, {
|
|
197
219
|
method: "POST",
|
|
198
|
-
body: JSON.stringify({ email })
|
|
220
|
+
body: JSON.stringify({ email: trimmedEmail })
|
|
199
221
|
});
|
|
200
222
|
setSent(true);
|
|
201
223
|
} catch (err) {
|
|
202
|
-
|
|
224
|
+
const message = err instanceof Error ? err.message : "Failed to request reset";
|
|
225
|
+
setError(message);
|
|
203
226
|
}
|
|
204
227
|
};
|
|
205
228
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
@@ -239,6 +262,7 @@ function PasswordResetScreen() {
|
|
|
239
262
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
240
263
|
"label",
|
|
241
264
|
{
|
|
265
|
+
htmlFor: "afk-reset-email",
|
|
242
266
|
style: {
|
|
243
267
|
position: "absolute",
|
|
244
268
|
top: sent ? "-18px" : "-10px",
|
|
@@ -255,6 +279,7 @@ function PasswordResetScreen() {
|
|
|
255
279
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
256
280
|
"input",
|
|
257
281
|
{
|
|
282
|
+
id: "afk-reset-email",
|
|
258
283
|
value: email,
|
|
259
284
|
onChange: (e) => setEmail(e.target.value),
|
|
260
285
|
type: "email",
|
|
@@ -317,6 +342,8 @@ function PasswordResetScreen() {
|
|
|
317
342
|
error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
318
343
|
"p",
|
|
319
344
|
{
|
|
345
|
+
role: "alert",
|
|
346
|
+
"aria-live": "polite",
|
|
320
347
|
style: {
|
|
321
348
|
marginTop: 20,
|
|
322
349
|
color: "crimson",
|
|
@@ -367,15 +394,26 @@ function LoginScreen() {
|
|
|
367
394
|
setError(null);
|
|
368
395
|
const trimmedEmail = email.trim();
|
|
369
396
|
const trimmedPassword = password.trim();
|
|
370
|
-
if (!trimmedEmail
|
|
371
|
-
setError("Email
|
|
397
|
+
if (!trimmedEmail) {
|
|
398
|
+
setError("Email is required.");
|
|
399
|
+
setSubmitting(false);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
|
|
403
|
+
setError("Enter a valid email address.");
|
|
404
|
+
setSubmitting(false);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (!trimmedPassword) {
|
|
408
|
+
setError("Password is required.");
|
|
372
409
|
setSubmitting(false);
|
|
373
410
|
return;
|
|
374
411
|
}
|
|
375
412
|
try {
|
|
376
413
|
await login(trimmedEmail, trimmedPassword);
|
|
377
414
|
} catch (err) {
|
|
378
|
-
|
|
415
|
+
const message = err instanceof Error ? err.message : "Login failed";
|
|
416
|
+
setError(message);
|
|
379
417
|
} finally {
|
|
380
418
|
setSubmitting(false);
|
|
381
419
|
}
|
|
@@ -416,6 +454,7 @@ function LoginScreen() {
|
|
|
416
454
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
417
455
|
"label",
|
|
418
456
|
{
|
|
457
|
+
htmlFor: "afk-login-email",
|
|
419
458
|
style: {
|
|
420
459
|
position: "absolute",
|
|
421
460
|
top: "-10px",
|
|
@@ -432,6 +471,7 @@ function LoginScreen() {
|
|
|
432
471
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
433
472
|
"input",
|
|
434
473
|
{
|
|
474
|
+
id: "afk-login-email",
|
|
435
475
|
value: email,
|
|
436
476
|
onChange: (e) => setEmail(e.target.value),
|
|
437
477
|
type: "email",
|
|
@@ -461,6 +501,7 @@ function LoginScreen() {
|
|
|
461
501
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
462
502
|
"label",
|
|
463
503
|
{
|
|
504
|
+
htmlFor: "afk-login-password",
|
|
464
505
|
style: {
|
|
465
506
|
position: "absolute",
|
|
466
507
|
top: "-10px",
|
|
@@ -477,6 +518,7 @@ function LoginScreen() {
|
|
|
477
518
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
478
519
|
"input",
|
|
479
520
|
{
|
|
521
|
+
id: "afk-login-password",
|
|
480
522
|
value: password,
|
|
481
523
|
onChange: (e) => setPassword(e.target.value),
|
|
482
524
|
type: "password",
|
|
@@ -503,17 +545,28 @@ function LoginScreen() {
|
|
|
503
545
|
)
|
|
504
546
|
] }),
|
|
505
547
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
506
|
-
"
|
|
548
|
+
"div",
|
|
507
549
|
{
|
|
508
|
-
onClick: () => setShowReset(true),
|
|
509
550
|
style: {
|
|
510
|
-
textAlign: "right"
|
|
511
|
-
fontSize: 13,
|
|
512
|
-
color: "#4b4bff",
|
|
513
|
-
cursor: "pointer",
|
|
514
|
-
marginBottom: 24
|
|
551
|
+
textAlign: "right"
|
|
515
552
|
},
|
|
516
|
-
children:
|
|
553
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
554
|
+
"button",
|
|
555
|
+
{
|
|
556
|
+
onClick: () => setShowReset(true),
|
|
557
|
+
style: {
|
|
558
|
+
textAlign: "right",
|
|
559
|
+
fontSize: 13,
|
|
560
|
+
color: "#4b4bff",
|
|
561
|
+
cursor: "pointer",
|
|
562
|
+
marginBottom: 24,
|
|
563
|
+
width: "fit-content",
|
|
564
|
+
border: "none",
|
|
565
|
+
background: "none"
|
|
566
|
+
},
|
|
567
|
+
children: "Forgot password?"
|
|
568
|
+
}
|
|
569
|
+
)
|
|
517
570
|
}
|
|
518
571
|
),
|
|
519
572
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -541,6 +594,8 @@ function LoginScreen() {
|
|
|
541
594
|
error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
542
595
|
"p",
|
|
543
596
|
{
|
|
597
|
+
role: "alert",
|
|
598
|
+
"aria-live": "polite",
|
|
544
599
|
style: {
|
|
545
600
|
marginTop: 18,
|
|
546
601
|
color: "crimson",
|
|
@@ -564,8 +619,21 @@ function SignupScreen() {
|
|
|
564
619
|
const [name, setName] = (0, import_react5.useState)("");
|
|
565
620
|
const [email, setEmail] = (0, import_react5.useState)("");
|
|
566
621
|
const [password, setPassword] = (0, import_react5.useState)("");
|
|
622
|
+
const [confirmPassword, setConfirmPassword] = (0, import_react5.useState)("");
|
|
567
623
|
const [submitting, setSubmitting] = (0, import_react5.useState)(false);
|
|
568
624
|
const [error, setError] = (0, import_react5.useState)(null);
|
|
625
|
+
const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
626
|
+
const getPasswordStrength = (value) => {
|
|
627
|
+
let score = 0;
|
|
628
|
+
if (value.length >= 8) score++;
|
|
629
|
+
if (/[A-Z]/.test(value)) score++;
|
|
630
|
+
if (/[0-9]/.test(value)) score++;
|
|
631
|
+
if (/[^A-Za-z0-9]/.test(value)) score++;
|
|
632
|
+
if (!value) return { label: "", color: "" };
|
|
633
|
+
if (score <= 1) return { label: "Weak", color: "crimson" };
|
|
634
|
+
if (score === 2) return { label: "Medium", color: "#f39c12" };
|
|
635
|
+
return { label: "Strong", color: "#2ecc71" };
|
|
636
|
+
};
|
|
569
637
|
const onSubmit = async (e) => {
|
|
570
638
|
e.preventDefault();
|
|
571
639
|
setSubmitting(true);
|
|
@@ -573,15 +641,47 @@ function SignupScreen() {
|
|
|
573
641
|
const trimmedEmail = email.trim();
|
|
574
642
|
const trimmedPassword = password.trim();
|
|
575
643
|
const trimmedName = name.trim();
|
|
576
|
-
|
|
577
|
-
|
|
644
|
+
const trimmedConfirm = confirmPassword.trim();
|
|
645
|
+
if (!trimmedName) {
|
|
646
|
+
setError("Name is required.");
|
|
647
|
+
setSubmitting(false);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (!trimmedEmail) {
|
|
651
|
+
setError("Email is required.");
|
|
652
|
+
setSubmitting(false);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (!isValidEmail(trimmedEmail)) {
|
|
656
|
+
setError("Enter a valid email address.");
|
|
657
|
+
setSubmitting(false);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (!trimmedPassword) {
|
|
661
|
+
setError("Password is required.");
|
|
662
|
+
setSubmitting(false);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (trimmedPassword.length < 8) {
|
|
666
|
+
setError("Password must be at least 8 characters long.");
|
|
667
|
+
setSubmitting(false);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (!trimmedConfirm) {
|
|
671
|
+
setError("Please confirm your password.");
|
|
672
|
+
setSubmitting(false);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
if (trimmedPassword !== trimmedConfirm) {
|
|
676
|
+
setError("Passwords do not match.");
|
|
578
677
|
setSubmitting(false);
|
|
579
678
|
return;
|
|
580
679
|
}
|
|
581
680
|
try {
|
|
582
|
-
await signup({ name, email, password });
|
|
681
|
+
await signup({ name: trimmedName, email: trimmedEmail, password: trimmedPassword });
|
|
583
682
|
} catch (err) {
|
|
584
|
-
|
|
683
|
+
const message = err instanceof Error ? err.message : "Signup failed";
|
|
684
|
+
setError(message);
|
|
585
685
|
} finally {
|
|
586
686
|
setSubmitting(false);
|
|
587
687
|
}
|
|
@@ -623,6 +723,7 @@ function SignupScreen() {
|
|
|
623
723
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
624
724
|
"label",
|
|
625
725
|
{
|
|
726
|
+
htmlFor: "afk-signup-name",
|
|
626
727
|
style: {
|
|
627
728
|
position: "absolute",
|
|
628
729
|
top: "-10px",
|
|
@@ -639,6 +740,7 @@ function SignupScreen() {
|
|
|
639
740
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
640
741
|
"input",
|
|
641
742
|
{
|
|
743
|
+
id: "afk-signup-name",
|
|
642
744
|
value: name,
|
|
643
745
|
onChange: (e) => setName(e.target.value),
|
|
644
746
|
placeholder: "Your name",
|
|
@@ -667,6 +769,7 @@ function SignupScreen() {
|
|
|
667
769
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
668
770
|
"label",
|
|
669
771
|
{
|
|
772
|
+
htmlFor: "afk-signup-email",
|
|
670
773
|
style: {
|
|
671
774
|
position: "absolute",
|
|
672
775
|
top: "-10px",
|
|
@@ -683,6 +786,7 @@ function SignupScreen() {
|
|
|
683
786
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
684
787
|
"input",
|
|
685
788
|
{
|
|
789
|
+
id: "afk-signup-email",
|
|
686
790
|
value: email,
|
|
687
791
|
onChange: (e) => setEmail(e.target.value),
|
|
688
792
|
type: "email",
|
|
@@ -712,6 +816,7 @@ function SignupScreen() {
|
|
|
712
816
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
713
817
|
"label",
|
|
714
818
|
{
|
|
819
|
+
htmlFor: "afk-signup-password",
|
|
715
820
|
style: {
|
|
716
821
|
position: "absolute",
|
|
717
822
|
top: "-10px",
|
|
@@ -728,6 +833,7 @@ function SignupScreen() {
|
|
|
728
833
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
729
834
|
"input",
|
|
730
835
|
{
|
|
836
|
+
id: "afk-signup-password",
|
|
731
837
|
value: password,
|
|
732
838
|
onChange: (e) => setPassword(e.target.value),
|
|
733
839
|
type: "password",
|
|
@@ -753,6 +859,95 @@ function SignupScreen() {
|
|
|
753
859
|
}
|
|
754
860
|
)
|
|
755
861
|
] }),
|
|
862
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative", marginBottom: 18 }, children: [
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
864
|
+
"label",
|
|
865
|
+
{
|
|
866
|
+
htmlFor: "afk-signup-confirm-password",
|
|
867
|
+
style: {
|
|
868
|
+
position: "absolute",
|
|
869
|
+
top: "-10px",
|
|
870
|
+
left: "14px",
|
|
871
|
+
background: "rgba(255,255,255,0.85)",
|
|
872
|
+
padding: "0 6px",
|
|
873
|
+
fontSize: 13,
|
|
874
|
+
color: "#444",
|
|
875
|
+
borderRadius: 6
|
|
876
|
+
},
|
|
877
|
+
children: "Confirm password"
|
|
878
|
+
}
|
|
879
|
+
),
|
|
880
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
881
|
+
"input",
|
|
882
|
+
{
|
|
883
|
+
id: "afk-signup-confirm-password",
|
|
884
|
+
value: confirmPassword,
|
|
885
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
886
|
+
type: "password",
|
|
887
|
+
placeholder: "Repeat your password",
|
|
888
|
+
style: {
|
|
889
|
+
width: "80%",
|
|
890
|
+
padding: "14px 16px",
|
|
891
|
+
borderRadius: 12,
|
|
892
|
+
border: "1px solid #d2d2d2",
|
|
893
|
+
fontSize: 15,
|
|
894
|
+
outline: "none",
|
|
895
|
+
transition: "0.25s",
|
|
896
|
+
background: "rgba(255,255,255,0.85)"
|
|
897
|
+
},
|
|
898
|
+
onFocus: (e) => {
|
|
899
|
+
e.currentTarget.style.border = "1px solid #4b4bff";
|
|
900
|
+
e.currentTarget.style.boxShadow = "0 0 0 3px rgba(75,75,255,0.25)";
|
|
901
|
+
},
|
|
902
|
+
onBlur: (e) => {
|
|
903
|
+
e.currentTarget.style.border = "1px solid #d2d2d2";
|
|
904
|
+
e.currentTarget.style.boxShadow = "0 0 0 transparent";
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
)
|
|
908
|
+
] }),
|
|
909
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
910
|
+
"div",
|
|
911
|
+
{
|
|
912
|
+
style: {
|
|
913
|
+
marginBottom: 20,
|
|
914
|
+
fontSize: 12,
|
|
915
|
+
color: "#555"
|
|
916
|
+
},
|
|
917
|
+
children: [
|
|
918
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { marginBottom: 6 }, children: "Use at least 8 characters, including letters, numbers, and symbols." }),
|
|
919
|
+
password && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
920
|
+
"p",
|
|
921
|
+
{
|
|
922
|
+
style: {
|
|
923
|
+
fontWeight: 600,
|
|
924
|
+
color: getPasswordStrength(password).color
|
|
925
|
+
},
|
|
926
|
+
children: [
|
|
927
|
+
"Password strength: ",
|
|
928
|
+
getPasswordStrength(password).label
|
|
929
|
+
]
|
|
930
|
+
}
|
|
931
|
+
)
|
|
932
|
+
]
|
|
933
|
+
}
|
|
934
|
+
),
|
|
935
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
936
|
+
"p",
|
|
937
|
+
{
|
|
938
|
+
style: {
|
|
939
|
+
textAlign: "center",
|
|
940
|
+
fontSize: 13,
|
|
941
|
+
color: "#4b4bff",
|
|
942
|
+
cursor: "pointer",
|
|
943
|
+
marginBottom: 24
|
|
944
|
+
},
|
|
945
|
+
onClick: () => {
|
|
946
|
+
window.dispatchEvent(new CustomEvent("auth-flow-kit:navigate-to-login"));
|
|
947
|
+
},
|
|
948
|
+
children: "Already have an account? Sign in"
|
|
949
|
+
}
|
|
950
|
+
),
|
|
756
951
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
757
952
|
"button",
|
|
758
953
|
{
|
|
@@ -778,6 +973,8 @@ function SignupScreen() {
|
|
|
778
973
|
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
779
974
|
"p",
|
|
780
975
|
{
|
|
976
|
+
role: "alert",
|
|
977
|
+
"aria-live": "polite",
|
|
781
978
|
style: {
|
|
782
979
|
marginTop: 18,
|
|
783
980
|
color: "crimson",
|