@kendevelops/auth-flow-kit 1.3.2 → 1.3.4
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.js
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/http.ts
|
|
11
11
|
function makeURL(baseURL, path) {
|
|
12
|
-
|
|
12
|
+
const base = baseURL.replace(/\/$/, "");
|
|
13
|
+
const suffix = path.startsWith("/") ? path : `/${path}`;
|
|
14
|
+
return `${base}${suffix}`;
|
|
13
15
|
}
|
|
14
16
|
function getStoredAccessToken() {
|
|
15
17
|
try {
|
|
@@ -20,8 +22,11 @@ function getStoredAccessToken() {
|
|
|
20
22
|
}
|
|
21
23
|
function setStoredAccessToken(token) {
|
|
22
24
|
try {
|
|
23
|
-
if (token)
|
|
24
|
-
|
|
25
|
+
if (token) {
|
|
26
|
+
localStorage.setItem("afk_access_token", token);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
localStorage.removeItem("afk_access_token");
|
|
25
30
|
} catch {
|
|
26
31
|
}
|
|
27
32
|
}
|
|
@@ -30,20 +35,27 @@ async function httpJSON(url, opts = {}, withAuth = false) {
|
|
|
30
35
|
"Content-Type": "application/json"
|
|
31
36
|
};
|
|
32
37
|
if (withAuth) {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
38
|
+
const storedToken = getStoredAccessToken();
|
|
39
|
+
if (storedToken) {
|
|
40
|
+
headers["Authorization"] = `Bearer ${storedToken}`;
|
|
41
|
+
}
|
|
35
42
|
}
|
|
36
43
|
const res = await fetch(url, {
|
|
37
44
|
...opts,
|
|
38
|
-
headers: {
|
|
45
|
+
headers: {
|
|
46
|
+
...headers,
|
|
47
|
+
...opts.headers || {}
|
|
48
|
+
}
|
|
39
49
|
});
|
|
40
50
|
if (!res.ok) {
|
|
41
51
|
let message = `Request failed (${res.status})`;
|
|
42
52
|
const contentType = res.headers.get("content-type") || "";
|
|
43
53
|
if (contentType.includes("application/json")) {
|
|
44
54
|
try {
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
55
|
+
const body = await res.json();
|
|
56
|
+
if (body?.message) {
|
|
57
|
+
message = body.message;
|
|
58
|
+
}
|
|
47
59
|
} catch {
|
|
48
60
|
}
|
|
49
61
|
}
|
|
@@ -163,18 +175,29 @@ function PasswordResetScreen() {
|
|
|
163
175
|
const [email, setEmail] = useState2("");
|
|
164
176
|
const [sent, setSent] = useState2(false);
|
|
165
177
|
const [error, setError] = useState2(null);
|
|
178
|
+
const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
166
179
|
const requestReset = async (e) => {
|
|
167
180
|
e.preventDefault();
|
|
168
181
|
setError(null);
|
|
182
|
+
const trimmedEmail = email.trim();
|
|
183
|
+
if (!trimmedEmail) {
|
|
184
|
+
setError("Email is required.");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!isValidEmail(trimmedEmail)) {
|
|
188
|
+
setError("Enter a valid email address.");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
169
191
|
try {
|
|
170
192
|
const url = makeURL(config.baseURL, config.endpoints.forgot);
|
|
171
193
|
await httpJSON(url, {
|
|
172
194
|
method: "POST",
|
|
173
|
-
body: JSON.stringify({ email })
|
|
195
|
+
body: JSON.stringify({ email: trimmedEmail })
|
|
174
196
|
});
|
|
175
197
|
setSent(true);
|
|
176
198
|
} catch (err) {
|
|
177
|
-
|
|
199
|
+
const message = err instanceof Error ? err.message : "Failed to request reset";
|
|
200
|
+
setError(message);
|
|
178
201
|
}
|
|
179
202
|
};
|
|
180
203
|
return /* @__PURE__ */ jsxs(
|
|
@@ -214,6 +237,7 @@ function PasswordResetScreen() {
|
|
|
214
237
|
/* @__PURE__ */ jsx3(
|
|
215
238
|
"label",
|
|
216
239
|
{
|
|
240
|
+
htmlFor: "afk-reset-email",
|
|
217
241
|
style: {
|
|
218
242
|
position: "absolute",
|
|
219
243
|
top: sent ? "-18px" : "-10px",
|
|
@@ -230,6 +254,7 @@ function PasswordResetScreen() {
|
|
|
230
254
|
/* @__PURE__ */ jsx3(
|
|
231
255
|
"input",
|
|
232
256
|
{
|
|
257
|
+
id: "afk-reset-email",
|
|
233
258
|
value: email,
|
|
234
259
|
onChange: (e) => setEmail(e.target.value),
|
|
235
260
|
type: "email",
|
|
@@ -292,6 +317,8 @@ function PasswordResetScreen() {
|
|
|
292
317
|
error && /* @__PURE__ */ jsx3(
|
|
293
318
|
"p",
|
|
294
319
|
{
|
|
320
|
+
role: "alert",
|
|
321
|
+
"aria-live": "polite",
|
|
295
322
|
style: {
|
|
296
323
|
marginTop: 20,
|
|
297
324
|
color: "crimson",
|
|
@@ -342,15 +369,26 @@ function LoginScreen() {
|
|
|
342
369
|
setError(null);
|
|
343
370
|
const trimmedEmail = email.trim();
|
|
344
371
|
const trimmedPassword = password.trim();
|
|
345
|
-
if (!trimmedEmail
|
|
346
|
-
setError("Email
|
|
372
|
+
if (!trimmedEmail) {
|
|
373
|
+
setError("Email is required.");
|
|
374
|
+
setSubmitting(false);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
|
|
378
|
+
setError("Enter a valid email address.");
|
|
379
|
+
setSubmitting(false);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (!trimmedPassword) {
|
|
383
|
+
setError("Password is required.");
|
|
347
384
|
setSubmitting(false);
|
|
348
385
|
return;
|
|
349
386
|
}
|
|
350
387
|
try {
|
|
351
388
|
await login(trimmedEmail, trimmedPassword);
|
|
352
389
|
} catch (err) {
|
|
353
|
-
|
|
390
|
+
const message = err instanceof Error ? err.message : "Login failed";
|
|
391
|
+
setError(message);
|
|
354
392
|
} finally {
|
|
355
393
|
setSubmitting(false);
|
|
356
394
|
}
|
|
@@ -391,6 +429,7 @@ function LoginScreen() {
|
|
|
391
429
|
/* @__PURE__ */ jsx4(
|
|
392
430
|
"label",
|
|
393
431
|
{
|
|
432
|
+
htmlFor: "afk-login-email",
|
|
394
433
|
style: {
|
|
395
434
|
position: "absolute",
|
|
396
435
|
top: "-10px",
|
|
@@ -407,6 +446,7 @@ function LoginScreen() {
|
|
|
407
446
|
/* @__PURE__ */ jsx4(
|
|
408
447
|
"input",
|
|
409
448
|
{
|
|
449
|
+
id: "afk-login-email",
|
|
410
450
|
value: email,
|
|
411
451
|
onChange: (e) => setEmail(e.target.value),
|
|
412
452
|
type: "email",
|
|
@@ -436,6 +476,7 @@ function LoginScreen() {
|
|
|
436
476
|
/* @__PURE__ */ jsx4(
|
|
437
477
|
"label",
|
|
438
478
|
{
|
|
479
|
+
htmlFor: "afk-login-password",
|
|
439
480
|
style: {
|
|
440
481
|
position: "absolute",
|
|
441
482
|
top: "-10px",
|
|
@@ -452,6 +493,7 @@ function LoginScreen() {
|
|
|
452
493
|
/* @__PURE__ */ jsx4(
|
|
453
494
|
"input",
|
|
454
495
|
{
|
|
496
|
+
id: "afk-login-password",
|
|
455
497
|
value: password,
|
|
456
498
|
onChange: (e) => setPassword(e.target.value),
|
|
457
499
|
type: "password",
|
|
@@ -478,17 +520,28 @@ function LoginScreen() {
|
|
|
478
520
|
)
|
|
479
521
|
] }),
|
|
480
522
|
/* @__PURE__ */ jsx4(
|
|
481
|
-
"
|
|
523
|
+
"div",
|
|
482
524
|
{
|
|
483
|
-
onClick: () => setShowReset(true),
|
|
484
525
|
style: {
|
|
485
|
-
textAlign: "right"
|
|
486
|
-
fontSize: 13,
|
|
487
|
-
color: "#4b4bff",
|
|
488
|
-
cursor: "pointer",
|
|
489
|
-
marginBottom: 24
|
|
526
|
+
textAlign: "right"
|
|
490
527
|
},
|
|
491
|
-
children:
|
|
528
|
+
children: /* @__PURE__ */ jsx4(
|
|
529
|
+
"button",
|
|
530
|
+
{
|
|
531
|
+
onClick: () => setShowReset(true),
|
|
532
|
+
style: {
|
|
533
|
+
textAlign: "right",
|
|
534
|
+
fontSize: 13,
|
|
535
|
+
color: "#4b4bff",
|
|
536
|
+
cursor: "pointer",
|
|
537
|
+
marginBottom: 24,
|
|
538
|
+
width: "fit-content",
|
|
539
|
+
border: "none",
|
|
540
|
+
background: "none"
|
|
541
|
+
},
|
|
542
|
+
children: "Forgot password?"
|
|
543
|
+
}
|
|
544
|
+
)
|
|
492
545
|
}
|
|
493
546
|
),
|
|
494
547
|
/* @__PURE__ */ jsx4(
|
|
@@ -516,6 +569,8 @@ function LoginScreen() {
|
|
|
516
569
|
error && /* @__PURE__ */ jsx4(
|
|
517
570
|
"p",
|
|
518
571
|
{
|
|
572
|
+
role: "alert",
|
|
573
|
+
"aria-live": "polite",
|
|
519
574
|
style: {
|
|
520
575
|
marginTop: 18,
|
|
521
576
|
color: "crimson",
|
|
@@ -539,8 +594,21 @@ function SignupScreen() {
|
|
|
539
594
|
const [name, setName] = useState4("");
|
|
540
595
|
const [email, setEmail] = useState4("");
|
|
541
596
|
const [password, setPassword] = useState4("");
|
|
597
|
+
const [confirmPassword, setConfirmPassword] = useState4("");
|
|
542
598
|
const [submitting, setSubmitting] = useState4(false);
|
|
543
599
|
const [error, setError] = useState4(null);
|
|
600
|
+
const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
601
|
+
const getPasswordStrength = (value) => {
|
|
602
|
+
let score = 0;
|
|
603
|
+
if (value.length >= 8) score++;
|
|
604
|
+
if (/[A-Z]/.test(value)) score++;
|
|
605
|
+
if (/[0-9]/.test(value)) score++;
|
|
606
|
+
if (/[^A-Za-z0-9]/.test(value)) score++;
|
|
607
|
+
if (!value) return { label: "", color: "" };
|
|
608
|
+
if (score <= 1) return { label: "Weak", color: "crimson" };
|
|
609
|
+
if (score === 2) return { label: "Medium", color: "#f39c12" };
|
|
610
|
+
return { label: "Strong", color: "#2ecc71" };
|
|
611
|
+
};
|
|
544
612
|
const onSubmit = async (e) => {
|
|
545
613
|
e.preventDefault();
|
|
546
614
|
setSubmitting(true);
|
|
@@ -548,15 +616,47 @@ function SignupScreen() {
|
|
|
548
616
|
const trimmedEmail = email.trim();
|
|
549
617
|
const trimmedPassword = password.trim();
|
|
550
618
|
const trimmedName = name.trim();
|
|
551
|
-
|
|
552
|
-
|
|
619
|
+
const trimmedConfirm = confirmPassword.trim();
|
|
620
|
+
if (!trimmedName) {
|
|
621
|
+
setError("Name is required.");
|
|
622
|
+
setSubmitting(false);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (!trimmedEmail) {
|
|
626
|
+
setError("Email is required.");
|
|
627
|
+
setSubmitting(false);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (!isValidEmail(trimmedEmail)) {
|
|
631
|
+
setError("Enter a valid email address.");
|
|
632
|
+
setSubmitting(false);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (!trimmedPassword) {
|
|
636
|
+
setError("Password is required.");
|
|
637
|
+
setSubmitting(false);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (trimmedPassword.length < 8) {
|
|
641
|
+
setError("Password must be at least 8 characters long.");
|
|
642
|
+
setSubmitting(false);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
if (!trimmedConfirm) {
|
|
646
|
+
setError("Please confirm your password.");
|
|
647
|
+
setSubmitting(false);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (trimmedPassword !== trimmedConfirm) {
|
|
651
|
+
setError("Passwords do not match.");
|
|
553
652
|
setSubmitting(false);
|
|
554
653
|
return;
|
|
555
654
|
}
|
|
556
655
|
try {
|
|
557
|
-
await signup({ name, email, password });
|
|
656
|
+
await signup({ name: trimmedName, email: trimmedEmail, password: trimmedPassword });
|
|
558
657
|
} catch (err) {
|
|
559
|
-
|
|
658
|
+
const message = err instanceof Error ? err.message : "Signup failed";
|
|
659
|
+
setError(message);
|
|
560
660
|
} finally {
|
|
561
661
|
setSubmitting(false);
|
|
562
662
|
}
|
|
@@ -598,6 +698,7 @@ function SignupScreen() {
|
|
|
598
698
|
/* @__PURE__ */ jsx5(
|
|
599
699
|
"label",
|
|
600
700
|
{
|
|
701
|
+
htmlFor: "afk-signup-name",
|
|
601
702
|
style: {
|
|
602
703
|
position: "absolute",
|
|
603
704
|
top: "-10px",
|
|
@@ -614,6 +715,7 @@ function SignupScreen() {
|
|
|
614
715
|
/* @__PURE__ */ jsx5(
|
|
615
716
|
"input",
|
|
616
717
|
{
|
|
718
|
+
id: "afk-signup-name",
|
|
617
719
|
value: name,
|
|
618
720
|
onChange: (e) => setName(e.target.value),
|
|
619
721
|
placeholder: "Your name",
|
|
@@ -642,6 +744,7 @@ function SignupScreen() {
|
|
|
642
744
|
/* @__PURE__ */ jsx5(
|
|
643
745
|
"label",
|
|
644
746
|
{
|
|
747
|
+
htmlFor: "afk-signup-email",
|
|
645
748
|
style: {
|
|
646
749
|
position: "absolute",
|
|
647
750
|
top: "-10px",
|
|
@@ -658,6 +761,7 @@ function SignupScreen() {
|
|
|
658
761
|
/* @__PURE__ */ jsx5(
|
|
659
762
|
"input",
|
|
660
763
|
{
|
|
764
|
+
id: "afk-signup-email",
|
|
661
765
|
value: email,
|
|
662
766
|
onChange: (e) => setEmail(e.target.value),
|
|
663
767
|
type: "email",
|
|
@@ -687,6 +791,7 @@ function SignupScreen() {
|
|
|
687
791
|
/* @__PURE__ */ jsx5(
|
|
688
792
|
"label",
|
|
689
793
|
{
|
|
794
|
+
htmlFor: "afk-signup-password",
|
|
690
795
|
style: {
|
|
691
796
|
position: "absolute",
|
|
692
797
|
top: "-10px",
|
|
@@ -703,6 +808,7 @@ function SignupScreen() {
|
|
|
703
808
|
/* @__PURE__ */ jsx5(
|
|
704
809
|
"input",
|
|
705
810
|
{
|
|
811
|
+
id: "afk-signup-password",
|
|
706
812
|
value: password,
|
|
707
813
|
onChange: (e) => setPassword(e.target.value),
|
|
708
814
|
type: "password",
|
|
@@ -728,6 +834,95 @@ function SignupScreen() {
|
|
|
728
834
|
}
|
|
729
835
|
)
|
|
730
836
|
] }),
|
|
837
|
+
/* @__PURE__ */ jsxs3("div", { style: { position: "relative", marginBottom: 18 }, children: [
|
|
838
|
+
/* @__PURE__ */ jsx5(
|
|
839
|
+
"label",
|
|
840
|
+
{
|
|
841
|
+
htmlFor: "afk-signup-confirm-password",
|
|
842
|
+
style: {
|
|
843
|
+
position: "absolute",
|
|
844
|
+
top: "-10px",
|
|
845
|
+
left: "14px",
|
|
846
|
+
background: "rgba(255,255,255,0.85)",
|
|
847
|
+
padding: "0 6px",
|
|
848
|
+
fontSize: 13,
|
|
849
|
+
color: "#444",
|
|
850
|
+
borderRadius: 6
|
|
851
|
+
},
|
|
852
|
+
children: "Confirm password"
|
|
853
|
+
}
|
|
854
|
+
),
|
|
855
|
+
/* @__PURE__ */ jsx5(
|
|
856
|
+
"input",
|
|
857
|
+
{
|
|
858
|
+
id: "afk-signup-confirm-password",
|
|
859
|
+
value: confirmPassword,
|
|
860
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
861
|
+
type: "password",
|
|
862
|
+
placeholder: "Repeat your password",
|
|
863
|
+
style: {
|
|
864
|
+
width: "80%",
|
|
865
|
+
padding: "14px 16px",
|
|
866
|
+
borderRadius: 12,
|
|
867
|
+
border: "1px solid #d2d2d2",
|
|
868
|
+
fontSize: 15,
|
|
869
|
+
outline: "none",
|
|
870
|
+
transition: "0.25s",
|
|
871
|
+
background: "rgba(255,255,255,0.85)"
|
|
872
|
+
},
|
|
873
|
+
onFocus: (e) => {
|
|
874
|
+
e.currentTarget.style.border = "1px solid #4b4bff";
|
|
875
|
+
e.currentTarget.style.boxShadow = "0 0 0 3px rgba(75,75,255,0.25)";
|
|
876
|
+
},
|
|
877
|
+
onBlur: (e) => {
|
|
878
|
+
e.currentTarget.style.border = "1px solid #d2d2d2";
|
|
879
|
+
e.currentTarget.style.boxShadow = "0 0 0 transparent";
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
)
|
|
883
|
+
] }),
|
|
884
|
+
/* @__PURE__ */ jsxs3(
|
|
885
|
+
"div",
|
|
886
|
+
{
|
|
887
|
+
style: {
|
|
888
|
+
marginBottom: 20,
|
|
889
|
+
fontSize: 12,
|
|
890
|
+
color: "#555"
|
|
891
|
+
},
|
|
892
|
+
children: [
|
|
893
|
+
/* @__PURE__ */ jsx5("p", { style: { marginBottom: 6 }, children: "Use at least 8 characters, including letters, numbers, and symbols." }),
|
|
894
|
+
password && /* @__PURE__ */ jsxs3(
|
|
895
|
+
"p",
|
|
896
|
+
{
|
|
897
|
+
style: {
|
|
898
|
+
fontWeight: 600,
|
|
899
|
+
color: getPasswordStrength(password).color
|
|
900
|
+
},
|
|
901
|
+
children: [
|
|
902
|
+
"Password strength: ",
|
|
903
|
+
getPasswordStrength(password).label
|
|
904
|
+
]
|
|
905
|
+
}
|
|
906
|
+
)
|
|
907
|
+
]
|
|
908
|
+
}
|
|
909
|
+
),
|
|
910
|
+
/* @__PURE__ */ jsx5(
|
|
911
|
+
"p",
|
|
912
|
+
{
|
|
913
|
+
style: {
|
|
914
|
+
textAlign: "center",
|
|
915
|
+
fontSize: 13,
|
|
916
|
+
color: "#4b4bff",
|
|
917
|
+
cursor: "pointer",
|
|
918
|
+
marginBottom: 24
|
|
919
|
+
},
|
|
920
|
+
onClick: () => {
|
|
921
|
+
window.dispatchEvent(new CustomEvent("auth-flow-kit:navigate-to-login"));
|
|
922
|
+
},
|
|
923
|
+
children: "Already have an account? Sign in"
|
|
924
|
+
}
|
|
925
|
+
),
|
|
731
926
|
/* @__PURE__ */ jsx5(
|
|
732
927
|
"button",
|
|
733
928
|
{
|
|
@@ -753,6 +948,8 @@ function SignupScreen() {
|
|
|
753
948
|
error && /* @__PURE__ */ jsx5(
|
|
754
949
|
"p",
|
|
755
950
|
{
|
|
951
|
+
role: "alert",
|
|
952
|
+
"aria-live": "polite",
|
|
756
953
|
style: {
|
|
757
954
|
marginTop: 18,
|
|
758
955
|
color: "crimson",
|