Harden admin login Turnstile flow

This commit is contained in:
Patrick Britton 2026-02-16 12:24:20 -06:00
parent e0fd5cea8c
commit aec14bc97d
2 changed files with 15 additions and 0 deletions

View file

@ -14,6 +14,7 @@ export default function LoginPage() {
const [loading, setLoading] = useState(false);
const [turnstileToken, setTurnstileToken] = useState('');
const [turnstileReady, setTurnstileReady] = useState(false);
const [turnstileWidgetRendered, setTurnstileWidgetRendered] = useState(false);
const turnstileRef = useRef<HTMLDivElement>(null);
const widgetIdRef = useRef<string | null>(null);
const tokenRef = useRef('');
@ -57,6 +58,7 @@ export default function LoginPage() {
'error-callback': () => { setTurnstileToken(''); tokenRef.current = ''; setTurnstileReady(false); },
'expired-callback': () => { setTurnstileToken(''); tokenRef.current = ''; setTurnstileReady(false); },
});
setTurnstileWidgetRendered(true);
}, [performLogin]);
useEffect(() => {
@ -69,6 +71,7 @@ export default function LoginPage() {
setTurnstileToken('');
tokenRef.current = '';
setTurnstileReady(false);
setTurnstileWidgetRendered(false);
setError('');
if (widgetIdRef.current && (window as any).turnstile) {
(window as any).turnstile.reset(widgetIdRef.current);
@ -82,6 +85,11 @@ export default function LoginPage() {
// Invisible Turnstile flow:
// - If we don't have a token yet, execute Turnstile first.
// - If we already have a token, proceed with login.
if (TURNSTILE_SITE_KEY && !widgetIdRef.current) {
setError('Security check is still loading. Please wait a moment and try again.');
return;
}
if (TURNSTILE_SITE_KEY && widgetIdRef.current && !tokenRef.current) {
setLoading(true);
try {
@ -95,6 +103,12 @@ export default function LoginPage() {
return;
}
// If Turnstile is enabled, we must have a token at this point.
if (TURNSTILE_SITE_KEY && !tokenRef.current) {
setError('Security verification failed. Please try again.');
return;
}
await performLogin();
};

View file

@ -84,6 +84,7 @@ func (h *AdminHandler) AdminLogin(c *gin.Context) {
// Verify Turnstile token
if h.turnstileSecret != "" {
if strings.TrimSpace(req.TurnstileToken) == "" {
log.Warn().Str("email", req.Email).Msg("Admin login: missing Turnstile token")
c.JSON(http.StatusBadRequest, gin.H{"error": "Security verification failed"})
return
}