@authagonal/login 0.3.8 → 0.3.10
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/api.d.ts +2 -2
- package/dist/index.js +69 -32
- package/package.json +1 -1
- package/src/api.ts +4 -4
- package/src/i18n/de.json +1 -0
- package/src/i18n/en.json +1 -0
- package/src/i18n/es.json +1 -0
- package/src/i18n/fr.json +1 -0
- package/src/i18n/pt.json +1 -0
- package/src/i18n/tlh.json +1 -0
- package/src/i18n/vi.json +1 -0
- package/src/i18n/zh-Hans.json +1 -0
- package/src/pages/ForgotPasswordPage.tsx +34 -7
- package/src/pages/LoginPage.tsx +1 -1
- package/src/pages/RegisterPage.tsx +1 -1
- package/src/pages/ResetPasswordPage.tsx +28 -3
package/dist/api.d.ts
CHANGED
|
@@ -10,10 +10,10 @@ export declare function register(email: string, password: string, firstName?: st
|
|
|
10
10
|
export declare function logout(): Promise<{
|
|
11
11
|
success: true;
|
|
12
12
|
}>;
|
|
13
|
-
export declare function forgotPassword(email: string): Promise<{
|
|
13
|
+
export declare function forgotPassword(email: string, turnstileToken?: string): Promise<{
|
|
14
14
|
success: true;
|
|
15
15
|
}>;
|
|
16
|
-
export declare function resetPassword(token: string, newPassword: string): Promise<{
|
|
16
|
+
export declare function resetPassword(token: string, newPassword: string, turnstileToken?: string): Promise<{
|
|
17
17
|
success: true;
|
|
18
18
|
}>;
|
|
19
19
|
export declare function getSession(): Promise<SessionResponse>;
|
package/dist/index.js
CHANGED
|
@@ -3523,6 +3523,7 @@ var Er = {
|
|
|
3523
3523
|
errorEmailRequired: "Email is required",
|
|
3524
3524
|
errorPasswordRequired: "Password is required",
|
|
3525
3525
|
errorUnexpected: "An unexpected error occurred. Please try again.",
|
|
3526
|
+
captchaFailed: "Verification failed. Please try again.",
|
|
3526
3527
|
resetYourPassword: "Reset your password",
|
|
3527
3528
|
resetSubtitle: "Enter your email address and we'll send you a link to reset your password.",
|
|
3528
3529
|
sending: "Sending...",
|
|
@@ -3656,6 +3657,7 @@ var Er = {
|
|
|
3656
3657
|
errorEmailRequired: "电子邮件为必填项",
|
|
3657
3658
|
errorPasswordRequired: "密码为必填项",
|
|
3658
3659
|
errorUnexpected: "发生意外错误,请重试。",
|
|
3660
|
+
captchaFailed: "验证失败,请重试。",
|
|
3659
3661
|
resetYourPassword: "重置密码",
|
|
3660
3662
|
resetSubtitle: "输入您的电子邮件地址,我们将向您发送重置密码的链接。",
|
|
3661
3663
|
sending: "正在发送...",
|
|
@@ -3766,6 +3768,7 @@ var Er = {
|
|
|
3766
3768
|
errorEmailRequired: "E-Mail ist erforderlich",
|
|
3767
3769
|
errorPasswordRequired: "Passwort ist erforderlich",
|
|
3768
3770
|
errorUnexpected: "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
|
|
3771
|
+
captchaFailed: "Verifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
|
3769
3772
|
resetYourPassword: "Passwort zurücksetzen",
|
|
3770
3773
|
resetSubtitle: "Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen Link zum Zurücksetzen Ihres Passworts.",
|
|
3771
3774
|
sending: "Wird gesendet...",
|
|
@@ -3876,6 +3879,7 @@ var Er = {
|
|
|
3876
3879
|
errorEmailRequired: "L'e-mail est requis",
|
|
3877
3880
|
errorPasswordRequired: "Le mot de passe est requis",
|
|
3878
3881
|
errorUnexpected: "Une erreur inattendue s'est produite. Veuillez réessayer.",
|
|
3882
|
+
captchaFailed: "Échec de la vérification. Veuillez réessayer.",
|
|
3879
3883
|
resetYourPassword: "Réinitialiser votre mot de passe",
|
|
3880
3884
|
resetSubtitle: "Entrez votre adresse e-mail et nous vous enverrons un lien pour réinitialiser votre mot de passe.",
|
|
3881
3885
|
sending: "Envoi en cours...",
|
|
@@ -3986,6 +3990,7 @@ var Er = {
|
|
|
3986
3990
|
errorEmailRequired: "El correo electrónico es obligatorio",
|
|
3987
3991
|
errorPasswordRequired: "La contraseña es obligatoria",
|
|
3988
3992
|
errorUnexpected: "Se produjo un error inesperado. Por favor, inténtelo de nuevo.",
|
|
3993
|
+
captchaFailed: "Error de verificación. Inténtalo de nuevo.",
|
|
3989
3994
|
resetYourPassword: "Restablecer su contraseña",
|
|
3990
3995
|
resetSubtitle: "Ingrese su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.",
|
|
3991
3996
|
sending: "Enviando...",
|
|
@@ -4096,6 +4101,7 @@ var Er = {
|
|
|
4096
4101
|
errorEmailRequired: "Email là bắt buộc",
|
|
4097
4102
|
errorPasswordRequired: "Mật khẩu là bắt buộc",
|
|
4098
4103
|
errorUnexpected: "Đã xảy ra lỗi không mong muốn. Vui lòng thử lại.",
|
|
4104
|
+
captchaFailed: "Xác minh không thành công. Vui lòng thử lại.",
|
|
4099
4105
|
resetYourPassword: "Đặt lại mật khẩu",
|
|
4100
4106
|
resetSubtitle: "Nhập địa chỉ email của bạn và chúng tôi sẽ gửi cho bạn liên kết để đặt lại mật khẩu.",
|
|
4101
4107
|
sending: "Đang gửi...",
|
|
@@ -4206,6 +4212,7 @@ var Er = {
|
|
|
4206
4212
|
errorEmailRequired: "O e-mail é obrigatório",
|
|
4207
4213
|
errorPasswordRequired: "A senha é obrigatória",
|
|
4208
4214
|
errorUnexpected: "Ocorreu um erro inesperado. Tente novamente.",
|
|
4215
|
+
captchaFailed: "Falha na verificação. Tente novamente.",
|
|
4209
4216
|
resetYourPassword: "Redefinir a sua senha",
|
|
4210
4217
|
resetSubtitle: "Introduza o seu endereço de e-mail e enviaremos um link para redefinir a sua senha.",
|
|
4211
4218
|
sending: "A enviar...",
|
|
@@ -4316,6 +4323,7 @@ var Er = {
|
|
|
4316
4323
|
errorEmailRequired: "QIn nab nIteb nISlu'",
|
|
4317
4324
|
errorPasswordRequired: "mu'wIj pegh nIteb nISlu'",
|
|
4318
4325
|
errorUnexpected: "Qagh qaS. yInIDqa'.",
|
|
4326
|
+
captchaFailed: "ngu' luj. yInIDqa'.",
|
|
4319
4327
|
resetYourPassword: "mu'wIj pegh yIchoH",
|
|
4320
4328
|
resetSubtitle: "QIn nab yIngu'. mu'wIj pegh choHmeH lIngwI' DangeH.",
|
|
4321
4329
|
sending: "ngeHmeH...",
|
|
@@ -4724,18 +4732,22 @@ function Yr(e, t, n, r, i) {
|
|
|
4724
4732
|
function Xr() {
|
|
4725
4733
|
return $("/api/auth/logout", { method: "POST" });
|
|
4726
4734
|
}
|
|
4727
|
-
function Zr(e) {
|
|
4735
|
+
function Zr(e, t) {
|
|
4728
4736
|
return $("/api/auth/forgot-password", {
|
|
4729
4737
|
method: "POST",
|
|
4730
|
-
body: JSON.stringify({
|
|
4738
|
+
body: JSON.stringify({
|
|
4739
|
+
email: e,
|
|
4740
|
+
turnstileToken: t
|
|
4741
|
+
})
|
|
4731
4742
|
});
|
|
4732
4743
|
}
|
|
4733
|
-
function Qr(e, t) {
|
|
4744
|
+
function Qr(e, t, n) {
|
|
4734
4745
|
return $("/api/auth/reset-password", {
|
|
4735
4746
|
method: "POST",
|
|
4736
4747
|
body: JSON.stringify({
|
|
4737
4748
|
token: e,
|
|
4738
|
-
newPassword: t
|
|
4749
|
+
newPassword: t,
|
|
4750
|
+
turnstileToken: n
|
|
4739
4751
|
})
|
|
4740
4752
|
});
|
|
4741
4753
|
}
|
|
@@ -4964,7 +4976,7 @@ function _i() {
|
|
|
4964
4976
|
C(e("errorPasswordRequired"));
|
|
4965
4977
|
break;
|
|
4966
4978
|
case "captcha_failed":
|
|
4967
|
-
C(e("
|
|
4979
|
+
C(e("captchaFailed"));
|
|
4968
4980
|
break;
|
|
4969
4981
|
default: C(t.message || e("errorUnexpected"));
|
|
4970
4982
|
}
|
|
@@ -5204,7 +5216,7 @@ function vi() {
|
|
|
5204
5216
|
b(e("errorEmailAndPasswordRequired"));
|
|
5205
5217
|
break;
|
|
5206
5218
|
case "captcha_failed":
|
|
5207
|
-
b(e("
|
|
5219
|
+
b(e("captchaFailed"));
|
|
5208
5220
|
break;
|
|
5209
5221
|
default: b(t.message || e("errorRegistrationFailed"));
|
|
5210
5222
|
}
|
|
@@ -5326,25 +5338,29 @@ function vi() {
|
|
|
5326
5338
|
//#endregion
|
|
5327
5339
|
//#region src/pages/ForgotPasswordPage.tsx
|
|
5328
5340
|
function yi() {
|
|
5329
|
-
let { t: e } = L(), [t] = _(), n = t.get("returnUrl") || "", [r, i] = s(""), [
|
|
5330
|
-
|
|
5331
|
-
|
|
5341
|
+
let { t: e } = L(), [t] = _(), n = t.get("returnUrl") || "", [r, i] = s(""), [o, c] = s(!1), [d, p] = s(!1), [m, h] = s(""), [g, v] = s(void 0), [y, b] = s(null), [x, S] = s(0);
|
|
5342
|
+
a(() => {
|
|
5343
|
+
ti().then((e) => v(e.turnstileSiteKey)).catch(() => {});
|
|
5344
|
+
}, []);
|
|
5345
|
+
let C = n ? `/login?returnUrl=${encodeURIComponent(n)}` : "/login";
|
|
5346
|
+
async function w(t) {
|
|
5347
|
+
t.preventDefault(), h(""), c(!0);
|
|
5332
5348
|
try {
|
|
5333
|
-
await Zr(r),
|
|
5334
|
-
} catch {
|
|
5335
|
-
|
|
5349
|
+
await Zr(r, y || void 0), p(!0);
|
|
5350
|
+
} catch (t) {
|
|
5351
|
+
t instanceof Kr && t.error === "captcha_failed" ? h(e("captchaFailed")) : h(e("errorUnexpected")), g && (b(null), S((e) => e + 1));
|
|
5336
5352
|
} finally {
|
|
5337
|
-
|
|
5353
|
+
c(!1);
|
|
5338
5354
|
}
|
|
5339
5355
|
}
|
|
5340
|
-
return
|
|
5356
|
+
return d ? /* @__PURE__ */ u("div", { children: [
|
|
5341
5357
|
/* @__PURE__ */ l(G, { children: e("checkYourEmail") }),
|
|
5342
5358
|
/* @__PURE__ */ l(Q, {
|
|
5343
5359
|
variant: "success",
|
|
5344
5360
|
children: e("resetEmailSent")
|
|
5345
5361
|
}),
|
|
5346
5362
|
/* @__PURE__ */ l(K, { children: /* @__PURE__ */ l(f, {
|
|
5347
|
-
to:
|
|
5363
|
+
to: C,
|
|
5348
5364
|
className: "text-sm font-medium text-primary hover:underline no-underline",
|
|
5349
5365
|
children: e("backToSignIn")
|
|
5350
5366
|
}) })
|
|
@@ -5354,12 +5370,12 @@ function yi() {
|
|
|
5354
5370
|
className: "mb-5",
|
|
5355
5371
|
children: e("resetSubtitle")
|
|
5356
5372
|
}),
|
|
5357
|
-
|
|
5373
|
+
m && /* @__PURE__ */ l(Q, {
|
|
5358
5374
|
variant: "error",
|
|
5359
|
-
children:
|
|
5375
|
+
children: m
|
|
5360
5376
|
}),
|
|
5361
5377
|
/* @__PURE__ */ u("form", {
|
|
5362
|
-
onSubmit:
|
|
5378
|
+
onSubmit: w,
|
|
5363
5379
|
children: [
|
|
5364
5380
|
/* @__PURE__ */ u("div", {
|
|
5365
5381
|
className: "mb-4",
|
|
@@ -5378,13 +5394,21 @@ function yi() {
|
|
|
5378
5394
|
required: !0
|
|
5379
5395
|
})]
|
|
5380
5396
|
}),
|
|
5397
|
+
g && /* @__PURE__ */ l("div", {
|
|
5398
|
+
className: "mb-4",
|
|
5399
|
+
children: /* @__PURE__ */ l(mi, {
|
|
5400
|
+
siteKey: g,
|
|
5401
|
+
onToken: b
|
|
5402
|
+
}, x)
|
|
5403
|
+
}),
|
|
5381
5404
|
/* @__PURE__ */ l(Y, {
|
|
5382
5405
|
type: "submit",
|
|
5383
|
-
loading:
|
|
5384
|
-
|
|
5406
|
+
loading: o,
|
|
5407
|
+
disabled: !!g && !y,
|
|
5408
|
+
children: e(o ? "sending" : "sendResetLink")
|
|
5385
5409
|
}),
|
|
5386
5410
|
/* @__PURE__ */ l(K, { children: /* @__PURE__ */ l(f, {
|
|
5387
|
-
to:
|
|
5411
|
+
to: C,
|
|
5388
5412
|
className: "text-sm font-medium text-primary hover:underline no-underline",
|
|
5389
5413
|
children: e("backToSignIn")
|
|
5390
5414
|
}) })
|
|
@@ -5449,13 +5473,15 @@ function Si(e, t) {
|
|
|
5449
5473
|
});
|
|
5450
5474
|
}
|
|
5451
5475
|
function Ci() {
|
|
5452
|
-
let { t: e } = L(), [t] = _(), n = t.get("p") || "", [r, i] = s(""), [o, c] = s(""), [d, p] = s(!1), [m, h] = s(""), [g, v] = s(!1), [y, b] = s(""), [x, S] = s(xi);
|
|
5476
|
+
let { t: e } = L(), [t] = _(), n = t.get("p") || "", [r, i] = s(""), [o, c] = s(""), [d, p] = s(!1), [m, h] = s(""), [g, v] = s(!1), [y, b] = s(""), [x, S] = s(xi), [C, w] = s(void 0), [T, E] = s(null), [D, O] = s(0);
|
|
5453
5477
|
a(() => {
|
|
5454
5478
|
fetch(`${bi}/api/auth/password-policy`).then((e) => e.ok ? e.json() : null).then((e) => {
|
|
5455
5479
|
e?.rules && S(e.rules);
|
|
5456
5480
|
}).catch(() => {});
|
|
5481
|
+
}, []), a(() => {
|
|
5482
|
+
ti().then((e) => w(e.turnstileSiteKey)).catch(() => {});
|
|
5457
5483
|
}, []);
|
|
5458
|
-
function
|
|
5484
|
+
function k(t) {
|
|
5459
5485
|
switch (t.rule) {
|
|
5460
5486
|
case "minLength": return e("ruleMinLength", { count: t.value ?? 8 });
|
|
5461
5487
|
case "uppercase": return e("ruleUppercase");
|
|
@@ -5465,12 +5491,12 @@ function Ci() {
|
|
|
5465
5491
|
default: return t.label;
|
|
5466
5492
|
}
|
|
5467
5493
|
}
|
|
5468
|
-
let
|
|
5494
|
+
let A = Si(r, x.map((e) => ({
|
|
5469
5495
|
...e,
|
|
5470
|
-
label:
|
|
5471
|
-
}))),
|
|
5472
|
-
async function
|
|
5473
|
-
if (t.preventDefault(), h(""), b(""), !
|
|
5496
|
+
label: k(e)
|
|
5497
|
+
}))), j = A.every((e) => e.met);
|
|
5498
|
+
async function M(t) {
|
|
5499
|
+
if (t.preventDefault(), h(""), b(""), !j) {
|
|
5474
5500
|
b(e("passwordNotMeetRequirements"));
|
|
5475
5501
|
return;
|
|
5476
5502
|
}
|
|
@@ -5480,7 +5506,7 @@ function Ci() {
|
|
|
5480
5506
|
}
|
|
5481
5507
|
p(!0);
|
|
5482
5508
|
try {
|
|
5483
|
-
await Qr(n, r), v(!0);
|
|
5509
|
+
await Qr(n, r, T || void 0), v(!0);
|
|
5484
5510
|
} catch (t) {
|
|
5485
5511
|
if (t instanceof Kr) switch (t.error) {
|
|
5486
5512
|
case "weak_password":
|
|
@@ -5493,9 +5519,13 @@ function Ci() {
|
|
|
5493
5519
|
case "password_required":
|
|
5494
5520
|
h(e("errorPasswordRequired"));
|
|
5495
5521
|
break;
|
|
5522
|
+
case "captcha_failed":
|
|
5523
|
+
h(e("captchaFailed"));
|
|
5524
|
+
break;
|
|
5496
5525
|
default: h(t.message || e("errorUnexpected"));
|
|
5497
5526
|
}
|
|
5498
5527
|
else h(e("errorUnexpected"));
|
|
5528
|
+
C && (E(null), O((e) => e + 1));
|
|
5499
5529
|
} finally {
|
|
5500
5530
|
p(!1);
|
|
5501
5531
|
}
|
|
@@ -5522,7 +5552,7 @@ function Ci() {
|
|
|
5522
5552
|
children: y
|
|
5523
5553
|
}),
|
|
5524
5554
|
/* @__PURE__ */ u("form", {
|
|
5525
|
-
onSubmit:
|
|
5555
|
+
onSubmit: M,
|
|
5526
5556
|
children: [
|
|
5527
5557
|
/* @__PURE__ */ u("div", {
|
|
5528
5558
|
className: "mb-4",
|
|
@@ -5543,7 +5573,7 @@ function Ci() {
|
|
|
5543
5573
|
}),
|
|
5544
5574
|
r.length > 0 && /* @__PURE__ */ l("ul", {
|
|
5545
5575
|
className: "list-none mb-4 p-3 bg-gray-50 dark:bg-gray-800/60 rounded-md",
|
|
5546
|
-
children:
|
|
5576
|
+
children: A.map((e) => /* @__PURE__ */ u("li", {
|
|
5547
5577
|
className: `text-[13px] py-0.5 flex items-center gap-1.5 ${e.met ? "text-green-800 dark:text-green-400" : "text-red-800 dark:text-red-400"}`,
|
|
5548
5578
|
children: [e.met ? /* @__PURE__ */ l(Xt, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ l(tn, { className: "h-3.5 w-3.5 shrink-0" }), e.label]
|
|
5549
5579
|
}, e.label))
|
|
@@ -5564,10 +5594,17 @@ function Ci() {
|
|
|
5564
5594
|
required: !0
|
|
5565
5595
|
})]
|
|
5566
5596
|
}),
|
|
5597
|
+
C && /* @__PURE__ */ l("div", {
|
|
5598
|
+
className: "mb-4",
|
|
5599
|
+
children: /* @__PURE__ */ l(mi, {
|
|
5600
|
+
siteKey: C,
|
|
5601
|
+
onToken: E
|
|
5602
|
+
}, D)
|
|
5603
|
+
}),
|
|
5567
5604
|
/* @__PURE__ */ l(Y, {
|
|
5568
5605
|
type: "submit",
|
|
5569
5606
|
loading: d,
|
|
5570
|
-
disabled: !T,
|
|
5607
|
+
disabled: !j || !!C && !T,
|
|
5571
5608
|
children: e(d ? "resetting" : "resetPassword")
|
|
5572
5609
|
}),
|
|
5573
5610
|
/* @__PURE__ */ l(K, { children: /* @__PURE__ */ l(f, {
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -66,17 +66,17 @@ export function logout(): Promise<{ success: true }> {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export function forgotPassword(email: string): Promise<{ success: true }> {
|
|
69
|
+
export function forgotPassword(email: string, turnstileToken?: string): Promise<{ success: true }> {
|
|
70
70
|
return api<{ success: true }>('/api/auth/forgot-password', {
|
|
71
71
|
method: 'POST',
|
|
72
|
-
body: JSON.stringify({ email }),
|
|
72
|
+
body: JSON.stringify({ email, turnstileToken }),
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export function resetPassword(token: string, newPassword: string): Promise<{ success: true }> {
|
|
76
|
+
export function resetPassword(token: string, newPassword: string, turnstileToken?: string): Promise<{ success: true }> {
|
|
77
77
|
return api<{ success: true }>('/api/auth/reset-password', {
|
|
78
78
|
method: 'POST',
|
|
79
|
-
body: JSON.stringify({ token, newPassword }),
|
|
79
|
+
body: JSON.stringify({ token, newPassword, turnstileToken }),
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
package/src/i18n/de.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "E-Mail ist erforderlich",
|
|
17
17
|
"errorPasswordRequired": "Passwort ist erforderlich",
|
|
18
18
|
"errorUnexpected": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es erneut.",
|
|
19
|
+
"captchaFailed": "Verifizierung fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
|
19
20
|
"resetYourPassword": "Passwort zurücksetzen",
|
|
20
21
|
"resetSubtitle": "Geben Sie Ihre E-Mail-Adresse ein und wir senden Ihnen einen Link zum Zurücksetzen Ihres Passworts.",
|
|
21
22
|
"sending": "Wird gesendet...",
|
package/src/i18n/en.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "Email is required",
|
|
17
17
|
"errorPasswordRequired": "Password is required",
|
|
18
18
|
"errorUnexpected": "An unexpected error occurred. Please try again.",
|
|
19
|
+
"captchaFailed": "Verification failed. Please try again.",
|
|
19
20
|
"resetYourPassword": "Reset your password",
|
|
20
21
|
"resetSubtitle": "Enter your email address and we'll send you a link to reset your password.",
|
|
21
22
|
"sending": "Sending...",
|
package/src/i18n/es.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "El correo electrónico es obligatorio",
|
|
17
17
|
"errorPasswordRequired": "La contraseña es obligatoria",
|
|
18
18
|
"errorUnexpected": "Se produjo un error inesperado. Por favor, inténtelo de nuevo.",
|
|
19
|
+
"captchaFailed": "Error de verificación. Inténtalo de nuevo.",
|
|
19
20
|
"resetYourPassword": "Restablecer su contraseña",
|
|
20
21
|
"resetSubtitle": "Ingrese su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.",
|
|
21
22
|
"sending": "Enviando...",
|
package/src/i18n/fr.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "L'e-mail est requis",
|
|
17
17
|
"errorPasswordRequired": "Le mot de passe est requis",
|
|
18
18
|
"errorUnexpected": "Une erreur inattendue s'est produite. Veuillez réessayer.",
|
|
19
|
+
"captchaFailed": "Échec de la vérification. Veuillez réessayer.",
|
|
19
20
|
"resetYourPassword": "Réinitialiser votre mot de passe",
|
|
20
21
|
"resetSubtitle": "Entrez votre adresse e-mail et nous vous enverrons un lien pour réinitialiser votre mot de passe.",
|
|
21
22
|
"sending": "Envoi en cours...",
|
package/src/i18n/pt.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "O e-mail é obrigatório",
|
|
17
17
|
"errorPasswordRequired": "A senha é obrigatória",
|
|
18
18
|
"errorUnexpected": "Ocorreu um erro inesperado. Tente novamente.",
|
|
19
|
+
"captchaFailed": "Falha na verificação. Tente novamente.",
|
|
19
20
|
"resetYourPassword": "Redefinir a sua senha",
|
|
20
21
|
"resetSubtitle": "Introduza o seu endereço de e-mail e enviaremos um link para redefinir a sua senha.",
|
|
21
22
|
"sending": "A enviar...",
|
package/src/i18n/tlh.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "QIn nab nIteb nISlu'",
|
|
17
17
|
"errorPasswordRequired": "mu'wIj pegh nIteb nISlu'",
|
|
18
18
|
"errorUnexpected": "Qagh qaS. yInIDqa'.",
|
|
19
|
+
"captchaFailed": "ngu' luj. yInIDqa'.",
|
|
19
20
|
"resetYourPassword": "mu'wIj pegh yIchoH",
|
|
20
21
|
"resetSubtitle": "QIn nab yIngu'. mu'wIj pegh choHmeH lIngwI' DangeH.",
|
|
21
22
|
"sending": "ngeHmeH...",
|
package/src/i18n/vi.json
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"errorEmailRequired": "Email là bắt buộc",
|
|
17
17
|
"errorPasswordRequired": "Mật khẩu là bắt buộc",
|
|
18
18
|
"errorUnexpected": "Đã xảy ra lỗi không mong muốn. Vui lòng thử lại.",
|
|
19
|
+
"captchaFailed": "Xác minh không thành công. Vui lòng thử lại.",
|
|
19
20
|
"resetYourPassword": "Đặt lại mật khẩu",
|
|
20
21
|
"resetSubtitle": "Nhập địa chỉ email của bạn và chúng tôi sẽ gửi cho bạn liên kết để đặt lại mật khẩu.",
|
|
21
22
|
"sending": "Đang gửi...",
|
package/src/i18n/zh-Hans.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
2
|
import { useSearchParams, Link } from 'react-router-dom';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { forgotPassword } from '../api';
|
|
4
|
+
import { forgotPassword, getProviders, ApiRequestError } from '../api';
|
|
5
|
+
import { Turnstile } from '../components/Turnstile';
|
|
5
6
|
import { Button } from '@/components/ui/button';
|
|
6
7
|
import { Input } from '@/components/ui/input';
|
|
7
8
|
import { Label } from '@/components/ui/label';
|
|
@@ -17,6 +18,16 @@ export default function ForgotPasswordPage() {
|
|
|
17
18
|
const [loading, setLoading] = useState(false);
|
|
18
19
|
const [submitted, setSubmitted] = useState(false);
|
|
19
20
|
const [error, setError] = useState('');
|
|
21
|
+
const [turnstileSiteKey, setTurnstileSiteKey] = useState<string | undefined>(undefined);
|
|
22
|
+
const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
|
|
23
|
+
const [turnstileKey, setTurnstileKey] = useState(0); // bump to re-mount the widget for a fresh challenge
|
|
24
|
+
|
|
25
|
+
// Surface the Turnstile site key (opt-in; empty when not configured for the tenant).
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
getProviders()
|
|
28
|
+
.then((res) => setTurnstileSiteKey(res.turnstileSiteKey))
|
|
29
|
+
.catch(() => {});
|
|
30
|
+
}, []);
|
|
20
31
|
|
|
21
32
|
const loginLink = returnUrl
|
|
22
33
|
? `/login?returnUrl=${encodeURIComponent(returnUrl)}`
|
|
@@ -28,11 +39,21 @@ export default function ForgotPasswordPage() {
|
|
|
28
39
|
setLoading(true);
|
|
29
40
|
|
|
30
41
|
try {
|
|
31
|
-
await forgotPassword(email);
|
|
42
|
+
await forgotPassword(email, turnstileToken || undefined);
|
|
32
43
|
setSubmitted(true);
|
|
33
|
-
} catch {
|
|
34
|
-
// The API always returns 200 for anti-enumeration
|
|
35
|
-
|
|
44
|
+
} catch (err) {
|
|
45
|
+
// The API always returns 200 for anti-enumeration; the only expected error is a
|
|
46
|
+
// failed captcha (handled explicitly), otherwise a generic message.
|
|
47
|
+
if (err instanceof ApiRequestError && err.error === 'captcha_failed') {
|
|
48
|
+
setError(t('captchaFailed'));
|
|
49
|
+
} else {
|
|
50
|
+
setError(t('errorUnexpected'));
|
|
51
|
+
}
|
|
52
|
+
// Turnstile tokens are single-use — reset so a retry gets a fresh challenge.
|
|
53
|
+
if (turnstileSiteKey) {
|
|
54
|
+
setTurnstileToken(null);
|
|
55
|
+
setTurnstileKey((k) => k + 1);
|
|
56
|
+
}
|
|
36
57
|
} finally {
|
|
37
58
|
setLoading(false);
|
|
38
59
|
}
|
|
@@ -75,7 +96,13 @@ export default function ForgotPasswordPage() {
|
|
|
75
96
|
/>
|
|
76
97
|
</div>
|
|
77
98
|
|
|
78
|
-
|
|
99
|
+
{turnstileSiteKey && (
|
|
100
|
+
<div className="mb-4">
|
|
101
|
+
<Turnstile key={turnstileKey} siteKey={turnstileSiteKey} onToken={setTurnstileToken} />
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<Button type="submit" loading={loading} disabled={!!turnstileSiteKey && !turnstileToken}>
|
|
79
106
|
{loading ? t('sending') : t('sendResetLink')}
|
|
80
107
|
</Button>
|
|
81
108
|
|
package/src/pages/LoginPage.tsx
CHANGED
|
@@ -224,7 +224,7 @@ export default function LoginPage() {
|
|
|
224
224
|
setError(t('errorPasswordRequired'));
|
|
225
225
|
break;
|
|
226
226
|
case 'captcha_failed':
|
|
227
|
-
setError(t('
|
|
227
|
+
setError(t('captchaFailed'));
|
|
228
228
|
break;
|
|
229
229
|
default:
|
|
230
230
|
setError(err.message || t('errorUnexpected'));
|
|
@@ -70,7 +70,7 @@ export default function RegisterPage() {
|
|
|
70
70
|
setError(t('errorEmailAndPasswordRequired'));
|
|
71
71
|
break;
|
|
72
72
|
case 'captcha_failed':
|
|
73
|
-
setError(t('
|
|
73
|
+
setError(t('captchaFailed'));
|
|
74
74
|
break;
|
|
75
75
|
default:
|
|
76
76
|
setError(err.message || t('errorRegistrationFailed'));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
import { useSearchParams, Link } from 'react-router-dom';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { resetPassword, ApiRequestError } from '../api';
|
|
4
|
+
import { resetPassword, getProviders, ApiRequestError } from '../api';
|
|
5
|
+
import { Turnstile } from '../components/Turnstile';
|
|
5
6
|
import { Button } from '@/components/ui/button';
|
|
6
7
|
import { Input } from '@/components/ui/input';
|
|
7
8
|
import { Label } from '@/components/ui/label';
|
|
@@ -57,6 +58,9 @@ export default function ResetPasswordPage() {
|
|
|
57
58
|
const [success, setSuccess] = useState(false);
|
|
58
59
|
const [validationError, setValidationError] = useState('');
|
|
59
60
|
const [rules, setRules] = useState<PasswordRule[]>(defaultRules);
|
|
61
|
+
const [turnstileSiteKey, setTurnstileSiteKey] = useState<string | undefined>(undefined);
|
|
62
|
+
const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
|
|
63
|
+
const [turnstileKey, setTurnstileKey] = useState(0); // bump to re-mount the widget for a fresh challenge
|
|
60
64
|
|
|
61
65
|
useEffect(() => {
|
|
62
66
|
fetch(`${API_URL}/api/auth/password-policy`)
|
|
@@ -65,6 +69,13 @@ export default function ResetPasswordPage() {
|
|
|
65
69
|
.catch(() => { /* use defaults */ });
|
|
66
70
|
}, []);
|
|
67
71
|
|
|
72
|
+
// Surface the Turnstile site key (opt-in; empty when not configured for the tenant).
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
getProviders()
|
|
75
|
+
.then((res) => setTurnstileSiteKey(res.turnstileSiteKey))
|
|
76
|
+
.catch(() => {});
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
68
79
|
function getRuleLabel(rule: PasswordRule): string {
|
|
69
80
|
switch (rule.rule) {
|
|
70
81
|
case 'minLength': return t('ruleMinLength', { count: rule.value ?? 8 });
|
|
@@ -102,7 +113,7 @@ export default function ResetPasswordPage() {
|
|
|
102
113
|
setLoading(true);
|
|
103
114
|
|
|
104
115
|
try {
|
|
105
|
-
await resetPassword(token, newPassword);
|
|
116
|
+
await resetPassword(token, newPassword, turnstileToken || undefined);
|
|
106
117
|
setSuccess(true);
|
|
107
118
|
} catch (err) {
|
|
108
119
|
if (err instanceof ApiRequestError) {
|
|
@@ -117,12 +128,20 @@ export default function ResetPasswordPage() {
|
|
|
117
128
|
case 'password_required':
|
|
118
129
|
setError(t('errorPasswordRequired'));
|
|
119
130
|
break;
|
|
131
|
+
case 'captcha_failed':
|
|
132
|
+
setError(t('captchaFailed'));
|
|
133
|
+
break;
|
|
120
134
|
default:
|
|
121
135
|
setError(err.message || t('errorUnexpected'));
|
|
122
136
|
}
|
|
123
137
|
} else {
|
|
124
138
|
setError(t('errorUnexpected'));
|
|
125
139
|
}
|
|
140
|
+
// Turnstile tokens are single-use — reset so a retry gets a fresh challenge.
|
|
141
|
+
if (turnstileSiteKey) {
|
|
142
|
+
setTurnstileToken(null);
|
|
143
|
+
setTurnstileKey((k) => k + 1);
|
|
144
|
+
}
|
|
126
145
|
} finally {
|
|
127
146
|
setLoading(false);
|
|
128
147
|
}
|
|
@@ -204,7 +223,13 @@ export default function ResetPasswordPage() {
|
|
|
204
223
|
/>
|
|
205
224
|
</div>
|
|
206
225
|
|
|
207
|
-
|
|
226
|
+
{turnstileSiteKey && (
|
|
227
|
+
<div className="mb-4">
|
|
228
|
+
<Turnstile key={turnstileKey} siteKey={turnstileSiteKey} onToken={setTurnstileToken} />
|
|
229
|
+
</div>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
<Button type="submit" loading={loading} disabled={!allRequirementsMet || (!!turnstileSiteKey && !turnstileToken)}>
|
|
208
233
|
{loading ? t('resetting') : t('resetPassword')}
|
|
209
234
|
</Button>
|
|
210
235
|
|