|
1 | 1 | 'use client' |
2 | 2 |
|
3 | | -import { useEffect, useRef, useState } from 'react' |
| 3 | +import { useEffect, useMemo, useRef, useState } from 'react' |
| 4 | +import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile' |
4 | 5 | import { createLogger } from '@sim/logger' |
5 | 6 | import { Eye, EyeOff } from 'lucide-react' |
6 | 7 | import Link from 'next/link' |
@@ -86,6 +87,9 @@ export default function LoginPage({ |
86 | 87 | const [password, setPassword] = useState('') |
87 | 88 | const [passwordErrors, setPasswordErrors] = useState<string[]>([]) |
88 | 89 | const [showValidationError, setShowValidationError] = useState(false) |
| 90 | + const [captchaToken, setCaptchaToken] = useState<string | null>(null) |
| 91 | + const turnstileRef = useRef<TurnstileInstance>(null) |
| 92 | + const turnstileSiteKey = useMemo(() => getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'), []) |
89 | 93 | const buttonClass = useBrandedButtonClass() |
90 | 94 |
|
91 | 95 | const callbackUrlParam = searchParams?.get('callbackUrl') |
@@ -185,7 +189,14 @@ export default function LoginPage({ |
185 | 189 | callbackURL: safeCallbackUrl, |
186 | 190 | }, |
187 | 191 | { |
| 192 | + fetchOptions: { |
| 193 | + headers: { |
| 194 | + ...(captchaToken ? { 'x-captcha-response': captchaToken } : {}), |
| 195 | + }, |
| 196 | + }, |
188 | 197 | onError: (ctx) => { |
| 198 | + turnstileRef.current?.reset() |
| 199 | + setCaptchaToken(null) |
189 | 200 | logger.error('Login error:', ctx.error) |
190 | 201 |
|
191 | 202 | if (ctx.error.code?.includes('EMAIL_NOT_VERIFIED')) { |
@@ -460,6 +471,17 @@ export default function LoginPage({ |
460 | 471 | </div> |
461 | 472 | </div> |
462 | 473 |
|
| 474 | + {turnstileSiteKey && ( |
| 475 | + <Turnstile |
| 476 | + ref={turnstileRef} |
| 477 | + siteKey={turnstileSiteKey} |
| 478 | + onSuccess={setCaptchaToken} |
| 479 | + onError={() => setCaptchaToken(null)} |
| 480 | + onExpire={() => setCaptchaToken(null)} |
| 481 | + options={{ size: 'invisible' }} |
| 482 | + /> |
| 483 | + )} |
| 484 | + |
463 | 485 | <BrandedButton |
464 | 486 | type='submit' |
465 | 487 | disabled={isLoading} |
|
0 commit comments