Improve CAPTCHA handling in signup methods
This commit refines the usage of CAPTCHA in signup methods by storing the CAPTCHA token and instance, providing a reset function, and managing validation on server-side. A new context instance has been introduced to keep track of the CAPTCHA instance besides the token. Proper error reporting is now in place for CAPTCHA verification failure.
This commit is contained in:
@@ -2,22 +2,31 @@
|
||||
|
||||
import { createContext, useState } from 'react';
|
||||
|
||||
import { TurnstileInstance } from '@marsidev/react-turnstile';
|
||||
|
||||
export const Captcha = createContext<{
|
||||
token: string;
|
||||
setToken: (token: string) => void;
|
||||
instance: TurnstileInstance | null;
|
||||
setInstance: (ref: TurnstileInstance) => void;
|
||||
}>({
|
||||
token: '',
|
||||
instance: null,
|
||||
setToken: (_: string) => {
|
||||
// do nothing
|
||||
return '';
|
||||
},
|
||||
setInstance: () => {
|
||||
// do nothing
|
||||
},
|
||||
});
|
||||
|
||||
export function CaptchaProvider(props: { children: React.ReactNode }) {
|
||||
const [token, setToken] = useState<string>('');
|
||||
const [instance, setInstance] = useState<TurnstileInstance | null>(null);
|
||||
|
||||
return (
|
||||
<Captcha.Provider value={{ token, setToken }}>
|
||||
<Captcha.Provider value={{ token, setToken, instance, setInstance }}>
|
||||
{props.children}
|
||||
</Captcha.Provider>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ export function CaptchaTokenSetter(props: {
|
||||
siteKey: string | undefined;
|
||||
options?: TurnstileProps;
|
||||
}) {
|
||||
const { setToken } = useContext(Captcha);
|
||||
const { setToken, setInstance } = useContext(Captcha);
|
||||
|
||||
if (!props.siteKey) {
|
||||
return null;
|
||||
@@ -23,6 +23,15 @@ export function CaptchaTokenSetter(props: {
|
||||
};
|
||||
|
||||
return (
|
||||
<Turnstile siteKey={props.siteKey} onSuccess={setToken} {...options} />
|
||||
<Turnstile
|
||||
ref={(instance) => {
|
||||
if (instance) {
|
||||
setInstance(instance);
|
||||
}
|
||||
}}
|
||||
siteKey={props.siteKey}
|
||||
onSuccess={setToken}
|
||||
{...options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { useContext } from 'react';
|
||||
import { useContext, useMemo } from 'react';
|
||||
|
||||
import { Captcha } from './captcha-provider';
|
||||
|
||||
/**
|
||||
* @name useCaptchaToken
|
||||
* @description A hook to get the captcha token and reset function
|
||||
* @returns The captcha token and reset function
|
||||
*/
|
||||
export function useCaptchaToken() {
|
||||
const context = useContext(Captcha);
|
||||
|
||||
@@ -9,5 +14,10 @@ export function useCaptchaToken() {
|
||||
throw new Error(`useCaptchaToken must be used within a CaptchaProvider`);
|
||||
}
|
||||
|
||||
return context.token;
|
||||
return useMemo(() => {
|
||||
return {
|
||||
captchaToken: context.token,
|
||||
resetCaptchaToken: () => context.instance?.reset(),
|
||||
};
|
||||
}, [context]);
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@ export async function verifyCaptchaToken(token: string) {
|
||||
const res = await fetch(verifyEndpoint, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error(`Captcha failed:`, res.statusText);
|
||||
|
||||
throw new Error('Failed to verify CAPTCHA token');
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!data.success) {
|
||||
|
||||
Reference in New Issue
Block a user