mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(platform): Turnstile CAPTCHA reset after failed login attempts (#10056)
Users were unable to retry login attempts after a failed authentication because the Turnstile CAPTCHA widget was not properly resetting. This forced users to refresh the entire page to attempt login again, creating a terrible user experience. Root Cause: The useTurnstile hook had several critical issues: - The reset() function only cleared state when shouldRender was true and widget existed - Widget ID tracking was unreliable due to intercepting window.turnstile.render - Token wasn't being cleared on verification failures - State wasn't being reset consistently across error scenarios Changes 🏗️ <!-- Concisely describe all of the changes made in this pull request: --> - Fixed useTurnstile hook reset logic: Modified the reset() function to always clear all state (token, verified, verifying, error) regardless of shouldRender condition - Improved widget ID synchronization: Added setWidgetId prop to the Turnstile component interface and hook for reliable widget tracking between component and hook - Enhanced error handling: Updated handleVerify, handleExpire, and handleError to properly reset tokens on failures - Updated all auth components: Added setWidgetId prop to all Turnstile component usages in login, signup, and password reset pages - Removed unreliable widget tracking: Eliminated the window.turnstile.render interception approach in favor of explicit prop-based communication Checklist 📋 For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - <!-- Put your test plan here: --> - [x] Test failed login attempt - CAPTCHA resets properly without page refresh - [x] Test failed signup attempt - CAPTCHA resets properly without page refresh - [x] Test successful login flow - CAPTCHA works normally - [x] Test CAPTCHA expiration - State resets correctly - [x] Test CAPTCHA error scenarios - Error handling works properly
This commit is contained in:
@@ -163,6 +163,7 @@ export default function LoginPage() {
|
||||
onVerify={turnstile.handleVerify}
|
||||
onExpire={turnstile.handleExpire}
|
||||
onError={turnstile.handleError}
|
||||
setWidgetId={turnstile.setWidgetId}
|
||||
action="login"
|
||||
shouldRender={turnstile.shouldRender}
|
||||
/>
|
||||
|
||||
@@ -188,6 +188,7 @@ export default function ResetPasswordPage() {
|
||||
onVerify={changePasswordTurnstile.handleVerify}
|
||||
onExpire={changePasswordTurnstile.handleExpire}
|
||||
onError={changePasswordTurnstile.handleError}
|
||||
setWidgetId={changePasswordTurnstile.setWidgetId}
|
||||
action="change_password"
|
||||
shouldRender={changePasswordTurnstile.shouldRender}
|
||||
/>
|
||||
@@ -230,6 +231,7 @@ export default function ResetPasswordPage() {
|
||||
onVerify={sendEmailTurnstile.handleVerify}
|
||||
onExpire={sendEmailTurnstile.handleExpire}
|
||||
onError={sendEmailTurnstile.handleError}
|
||||
setWidgetId={sendEmailTurnstile.setWidgetId}
|
||||
action="reset_password"
|
||||
shouldRender={sendEmailTurnstile.shouldRender}
|
||||
/>
|
||||
|
||||
@@ -164,6 +164,7 @@ export default function SignupPage() {
|
||||
onVerify={turnstile.handleVerify}
|
||||
onExpire={turnstile.handleExpire}
|
||||
onError={turnstile.handleError}
|
||||
setWidgetId={turnstile.setWidgetId}
|
||||
action="signup"
|
||||
shouldRender={turnstile.shouldRender}
|
||||
/>
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface TurnstileProps {
|
||||
className?: string;
|
||||
id?: string;
|
||||
shouldRender?: boolean;
|
||||
setWidgetId?: (id: string | null) => void;
|
||||
}
|
||||
|
||||
export function Turnstile({
|
||||
@@ -22,6 +23,7 @@ export function Turnstile({
|
||||
className,
|
||||
id = "cf-turnstile",
|
||||
shouldRender = true,
|
||||
setWidgetId,
|
||||
}: TurnstileProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const widgetIdRef = useRef<string | null>(null);
|
||||
@@ -68,7 +70,11 @@ export function Turnstile({
|
||||
|
||||
// Reset any existing widget
|
||||
if (widgetIdRef.current && window.turnstile) {
|
||||
window.turnstile.reset(widgetIdRef.current);
|
||||
try {
|
||||
window.turnstile.reset(widgetIdRef.current);
|
||||
} catch (err) {
|
||||
console.warn("Failed to reset existing Turnstile widget:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Render a new widget
|
||||
@@ -86,15 +92,32 @@ export function Turnstile({
|
||||
},
|
||||
action,
|
||||
});
|
||||
|
||||
// Notify the hook about the widget ID
|
||||
setWidgetId?.(widgetIdRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (widgetIdRef.current && window.turnstile) {
|
||||
window.turnstile.remove(widgetIdRef.current);
|
||||
try {
|
||||
window.turnstile.remove(widgetIdRef.current);
|
||||
} catch (err) {
|
||||
console.warn("Failed to remove Turnstile widget:", err);
|
||||
}
|
||||
setWidgetId?.(null);
|
||||
widgetIdRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [loaded, siteKey, onVerify, onExpire, onError, action, shouldRender]);
|
||||
}, [
|
||||
loaded,
|
||||
siteKey,
|
||||
onVerify,
|
||||
onExpire,
|
||||
onError,
|
||||
action,
|
||||
shouldRender,
|
||||
setWidgetId,
|
||||
]);
|
||||
|
||||
// Method to reset the widget manually
|
||||
const reset = useCallback(() => {
|
||||
|
||||
@@ -21,6 +21,7 @@ interface UseTurnstileResult {
|
||||
reset: () => void;
|
||||
siteKey: string;
|
||||
shouldRender: boolean;
|
||||
setWidgetId: (id: string | null) => void;
|
||||
}
|
||||
|
||||
const TURNSTILE_SITE_KEY =
|
||||
@@ -34,7 +35,7 @@ export function useTurnstile({
|
||||
autoVerify = true,
|
||||
onSuccess,
|
||||
onError,
|
||||
resetOnError = false,
|
||||
resetOnError = true,
|
||||
}: UseTurnstileOptions = {}): UseTurnstileResult {
|
||||
const [token, setToken] = useState<string | null>(null);
|
||||
const [verifying, setVerifying] = useState(false);
|
||||
@@ -60,26 +61,30 @@ export function useTurnstile({
|
||||
}
|
||||
}, [token, autoVerify, shouldRender]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined" && window.turnstile) {
|
||||
const originalRender = window.turnstile.render;
|
||||
window.turnstile.render = (container, options) => {
|
||||
const id = originalRender(container, options);
|
||||
setWidgetId(id);
|
||||
return id;
|
||||
};
|
||||
}
|
||||
const setWidgetIdCallback = useCallback((id: string | null) => {
|
||||
setWidgetId(id);
|
||||
}, []);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
if (shouldRender && window.turnstile && widgetId) {
|
||||
window.turnstile.reset(widgetId);
|
||||
// Always reset the state when reset is called, regardless of shouldRender
|
||||
// This ensures users can retry CAPTCHA after failed attempts
|
||||
setToken(null);
|
||||
setVerified(false);
|
||||
setVerifying(false);
|
||||
setError(null);
|
||||
|
||||
// Always reset the state when reset is called
|
||||
setToken(null);
|
||||
setVerified(false);
|
||||
setVerifying(false);
|
||||
setError(null);
|
||||
// Only reset the actual Turnstile widget if it exists and shouldRender is true
|
||||
if (
|
||||
shouldRender &&
|
||||
typeof window !== "undefined" &&
|
||||
window.turnstile &&
|
||||
widgetId
|
||||
) {
|
||||
try {
|
||||
window.turnstile.reset(widgetId);
|
||||
} catch (err) {
|
||||
console.warn("Failed to reset Turnstile widget:", err);
|
||||
}
|
||||
}
|
||||
}, [shouldRender, widgetId]);
|
||||
|
||||
@@ -106,6 +111,7 @@ export function useTurnstile({
|
||||
setError(newError);
|
||||
if (onError) onError(newError);
|
||||
if (resetOnError) {
|
||||
setToken(null);
|
||||
setVerified(false);
|
||||
}
|
||||
}
|
||||
@@ -119,6 +125,7 @@ export function useTurnstile({
|
||||
: new Error("Unknown error during verification");
|
||||
setError(newError);
|
||||
if (resetOnError) {
|
||||
setToken(null);
|
||||
setVerified(false);
|
||||
}
|
||||
setVerifying(false);
|
||||
@@ -138,6 +145,7 @@ export function useTurnstile({
|
||||
if (shouldRender) {
|
||||
setToken(null);
|
||||
setVerified(false);
|
||||
setError(null);
|
||||
}
|
||||
}, [shouldRender]);
|
||||
|
||||
@@ -146,6 +154,7 @@ export function useTurnstile({
|
||||
if (shouldRender) {
|
||||
setError(err);
|
||||
if (resetOnError) {
|
||||
setToken(null);
|
||||
setVerified(false);
|
||||
}
|
||||
if (onError) onError(err);
|
||||
@@ -165,5 +174,6 @@ export function useTurnstile({
|
||||
reset,
|
||||
siteKey: TURNSTILE_SITE_KEY,
|
||||
shouldRender,
|
||||
setWidgetId: setWidgetIdCallback,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user