@authagonal/login 0.3.8 → 0.3.9
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 +58 -29
- package/package.json +1 -1
- package/src/api.ts +4 -4
- package/src/pages/ForgotPasswordPage.tsx +26 -4
- 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
|
@@ -4724,18 +4724,22 @@ function Yr(e, t, n, r, i) {
|
|
|
4724
4724
|
function Xr() {
|
|
4725
4725
|
return $("/api/auth/logout", { method: "POST" });
|
|
4726
4726
|
}
|
|
4727
|
-
function Zr(e) {
|
|
4727
|
+
function Zr(e, t) {
|
|
4728
4728
|
return $("/api/auth/forgot-password", {
|
|
4729
4729
|
method: "POST",
|
|
4730
|
-
body: JSON.stringify({
|
|
4730
|
+
body: JSON.stringify({
|
|
4731
|
+
email: e,
|
|
4732
|
+
turnstileToken: t
|
|
4733
|
+
})
|
|
4731
4734
|
});
|
|
4732
4735
|
}
|
|
4733
|
-
function Qr(e, t) {
|
|
4736
|
+
function Qr(e, t, n) {
|
|
4734
4737
|
return $("/api/auth/reset-password", {
|
|
4735
4738
|
method: "POST",
|
|
4736
4739
|
body: JSON.stringify({
|
|
4737
4740
|
token: e,
|
|
4738
|
-
newPassword: t
|
|
4741
|
+
newPassword: t,
|
|
4742
|
+
turnstileToken: n
|
|
4739
4743
|
})
|
|
4740
4744
|
});
|
|
4741
4745
|
}
|
|
@@ -5326,25 +5330,29 @@ function vi() {
|
|
|
5326
5330
|
//#endregion
|
|
5327
5331
|
//#region src/pages/ForgotPasswordPage.tsx
|
|
5328
5332
|
function yi() {
|
|
5329
|
-
let { t: e } = L(), [t] = _(), n = t.get("returnUrl") || "", [r, i] = s(""), [
|
|
5330
|
-
|
|
5331
|
-
|
|
5333
|
+
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);
|
|
5334
|
+
a(() => {
|
|
5335
|
+
ti().then((e) => v(e.turnstileSiteKey)).catch(() => {});
|
|
5336
|
+
}, []);
|
|
5337
|
+
let C = n ? `/login?returnUrl=${encodeURIComponent(n)}` : "/login";
|
|
5338
|
+
async function w(t) {
|
|
5339
|
+
t.preventDefault(), h(""), c(!0);
|
|
5332
5340
|
try {
|
|
5333
|
-
await Zr(r),
|
|
5341
|
+
await Zr(r, y || void 0), p(!0);
|
|
5334
5342
|
} catch {
|
|
5335
|
-
|
|
5343
|
+
h(e("errorUnexpected")), g && (b(null), S((e) => e + 1));
|
|
5336
5344
|
} finally {
|
|
5337
|
-
|
|
5345
|
+
c(!1);
|
|
5338
5346
|
}
|
|
5339
5347
|
}
|
|
5340
|
-
return
|
|
5348
|
+
return d ? /* @__PURE__ */ u("div", { children: [
|
|
5341
5349
|
/* @__PURE__ */ l(G, { children: e("checkYourEmail") }),
|
|
5342
5350
|
/* @__PURE__ */ l(Q, {
|
|
5343
5351
|
variant: "success",
|
|
5344
5352
|
children: e("resetEmailSent")
|
|
5345
5353
|
}),
|
|
5346
5354
|
/* @__PURE__ */ l(K, { children: /* @__PURE__ */ l(f, {
|
|
5347
|
-
to:
|
|
5355
|
+
to: C,
|
|
5348
5356
|
className: "text-sm font-medium text-primary hover:underline no-underline",
|
|
5349
5357
|
children: e("backToSignIn")
|
|
5350
5358
|
}) })
|
|
@@ -5354,12 +5362,12 @@ function yi() {
|
|
|
5354
5362
|
className: "mb-5",
|
|
5355
5363
|
children: e("resetSubtitle")
|
|
5356
5364
|
}),
|
|
5357
|
-
|
|
5365
|
+
m && /* @__PURE__ */ l(Q, {
|
|
5358
5366
|
variant: "error",
|
|
5359
|
-
children:
|
|
5367
|
+
children: m
|
|
5360
5368
|
}),
|
|
5361
5369
|
/* @__PURE__ */ u("form", {
|
|
5362
|
-
onSubmit:
|
|
5370
|
+
onSubmit: w,
|
|
5363
5371
|
children: [
|
|
5364
5372
|
/* @__PURE__ */ u("div", {
|
|
5365
5373
|
className: "mb-4",
|
|
@@ -5378,13 +5386,21 @@ function yi() {
|
|
|
5378
5386
|
required: !0
|
|
5379
5387
|
})]
|
|
5380
5388
|
}),
|
|
5389
|
+
g && /* @__PURE__ */ l("div", {
|
|
5390
|
+
className: "mb-4",
|
|
5391
|
+
children: /* @__PURE__ */ l(mi, {
|
|
5392
|
+
siteKey: g,
|
|
5393
|
+
onToken: b
|
|
5394
|
+
}, x)
|
|
5395
|
+
}),
|
|
5381
5396
|
/* @__PURE__ */ l(Y, {
|
|
5382
5397
|
type: "submit",
|
|
5383
|
-
loading:
|
|
5384
|
-
|
|
5398
|
+
loading: o,
|
|
5399
|
+
disabled: !!g && !y,
|
|
5400
|
+
children: e(o ? "sending" : "sendResetLink")
|
|
5385
5401
|
}),
|
|
5386
5402
|
/* @__PURE__ */ l(K, { children: /* @__PURE__ */ l(f, {
|
|
5387
|
-
to:
|
|
5403
|
+
to: C,
|
|
5388
5404
|
className: "text-sm font-medium text-primary hover:underline no-underline",
|
|
5389
5405
|
children: e("backToSignIn")
|
|
5390
5406
|
}) })
|
|
@@ -5449,13 +5465,15 @@ function Si(e, t) {
|
|
|
5449
5465
|
});
|
|
5450
5466
|
}
|
|
5451
5467
|
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);
|
|
5468
|
+
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
5469
|
a(() => {
|
|
5454
5470
|
fetch(`${bi}/api/auth/password-policy`).then((e) => e.ok ? e.json() : null).then((e) => {
|
|
5455
5471
|
e?.rules && S(e.rules);
|
|
5456
5472
|
}).catch(() => {});
|
|
5473
|
+
}, []), a(() => {
|
|
5474
|
+
ti().then((e) => w(e.turnstileSiteKey)).catch(() => {});
|
|
5457
5475
|
}, []);
|
|
5458
|
-
function
|
|
5476
|
+
function k(t) {
|
|
5459
5477
|
switch (t.rule) {
|
|
5460
5478
|
case "minLength": return e("ruleMinLength", { count: t.value ?? 8 });
|
|
5461
5479
|
case "uppercase": return e("ruleUppercase");
|
|
@@ -5465,12 +5483,12 @@ function Ci() {
|
|
|
5465
5483
|
default: return t.label;
|
|
5466
5484
|
}
|
|
5467
5485
|
}
|
|
5468
|
-
let
|
|
5486
|
+
let A = Si(r, x.map((e) => ({
|
|
5469
5487
|
...e,
|
|
5470
|
-
label:
|
|
5471
|
-
}))),
|
|
5472
|
-
async function
|
|
5473
|
-
if (t.preventDefault(), h(""), b(""), !
|
|
5488
|
+
label: k(e)
|
|
5489
|
+
}))), j = A.every((e) => e.met);
|
|
5490
|
+
async function M(t) {
|
|
5491
|
+
if (t.preventDefault(), h(""), b(""), !j) {
|
|
5474
5492
|
b(e("passwordNotMeetRequirements"));
|
|
5475
5493
|
return;
|
|
5476
5494
|
}
|
|
@@ -5480,7 +5498,7 @@ function Ci() {
|
|
|
5480
5498
|
}
|
|
5481
5499
|
p(!0);
|
|
5482
5500
|
try {
|
|
5483
|
-
await Qr(n, r), v(!0);
|
|
5501
|
+
await Qr(n, r, T || void 0), v(!0);
|
|
5484
5502
|
} catch (t) {
|
|
5485
5503
|
if (t instanceof Kr) switch (t.error) {
|
|
5486
5504
|
case "weak_password":
|
|
@@ -5493,9 +5511,13 @@ function Ci() {
|
|
|
5493
5511
|
case "password_required":
|
|
5494
5512
|
h(e("errorPasswordRequired"));
|
|
5495
5513
|
break;
|
|
5514
|
+
case "captcha_failed":
|
|
5515
|
+
h(e("errorUnexpected"));
|
|
5516
|
+
break;
|
|
5496
5517
|
default: h(t.message || e("errorUnexpected"));
|
|
5497
5518
|
}
|
|
5498
5519
|
else h(e("errorUnexpected"));
|
|
5520
|
+
C && (E(null), O((e) => e + 1));
|
|
5499
5521
|
} finally {
|
|
5500
5522
|
p(!1);
|
|
5501
5523
|
}
|
|
@@ -5522,7 +5544,7 @@ function Ci() {
|
|
|
5522
5544
|
children: y
|
|
5523
5545
|
}),
|
|
5524
5546
|
/* @__PURE__ */ u("form", {
|
|
5525
|
-
onSubmit:
|
|
5547
|
+
onSubmit: M,
|
|
5526
5548
|
children: [
|
|
5527
5549
|
/* @__PURE__ */ u("div", {
|
|
5528
5550
|
className: "mb-4",
|
|
@@ -5543,7 +5565,7 @@ function Ci() {
|
|
|
5543
5565
|
}),
|
|
5544
5566
|
r.length > 0 && /* @__PURE__ */ l("ul", {
|
|
5545
5567
|
className: "list-none mb-4 p-3 bg-gray-50 dark:bg-gray-800/60 rounded-md",
|
|
5546
|
-
children:
|
|
5568
|
+
children: A.map((e) => /* @__PURE__ */ u("li", {
|
|
5547
5569
|
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
5570
|
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
5571
|
}, e.label))
|
|
@@ -5564,10 +5586,17 @@ function Ci() {
|
|
|
5564
5586
|
required: !0
|
|
5565
5587
|
})]
|
|
5566
5588
|
}),
|
|
5589
|
+
C && /* @__PURE__ */ l("div", {
|
|
5590
|
+
className: "mb-4",
|
|
5591
|
+
children: /* @__PURE__ */ l(mi, {
|
|
5592
|
+
siteKey: C,
|
|
5593
|
+
onToken: E
|
|
5594
|
+
}, D)
|
|
5595
|
+
}),
|
|
5567
5596
|
/* @__PURE__ */ l(Y, {
|
|
5568
5597
|
type: "submit",
|
|
5569
5598
|
loading: d,
|
|
5570
|
-
disabled: !T,
|
|
5599
|
+
disabled: !j || !!C && !T,
|
|
5571
5600
|
children: e(d ? "resetting" : "resetPassword")
|
|
5572
5601
|
}),
|
|
5573
5602
|
/* @__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
|
|
|
@@ -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 } 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,16 @@ 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
44
|
} catch {
|
|
34
45
|
// The API always returns 200 for anti-enumeration, but handle errors just in case
|
|
35
46
|
setError(t('errorUnexpected'));
|
|
47
|
+
// Turnstile tokens are single-use — reset so a retry gets a fresh challenge.
|
|
48
|
+
if (turnstileSiteKey) {
|
|
49
|
+
setTurnstileToken(null);
|
|
50
|
+
setTurnstileKey((k) => k + 1);
|
|
51
|
+
}
|
|
36
52
|
} finally {
|
|
37
53
|
setLoading(false);
|
|
38
54
|
}
|
|
@@ -75,7 +91,13 @@ export default function ForgotPasswordPage() {
|
|
|
75
91
|
/>
|
|
76
92
|
</div>
|
|
77
93
|
|
|
78
|
-
|
|
94
|
+
{turnstileSiteKey && (
|
|
95
|
+
<div className="mb-4">
|
|
96
|
+
<Turnstile key={turnstileKey} siteKey={turnstileSiteKey} onToken={setTurnstileToken} />
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
|
|
100
|
+
<Button type="submit" loading={loading} disabled={!!turnstileSiteKey && !turnstileToken}>
|
|
79
101
|
{loading ? t('sending') : t('sendResetLink')}
|
|
80
102
|
</Button>
|
|
81
103
|
|
|
@@ -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('errorUnexpected'));
|
|
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
|
|