feat(auth): integrate captcha validation and component for sign-in/up… (#15054)

… process

Added captcha token validation to the sign-in/up flow. Introduced
`StyledLoaderContainer` for better UI feedback during loading. Enhanced
error handling within `useSignInUp` to handle GraphQL errors
effectively.

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-10-24 10:13:52 +02:00
committed by GitHub
parent 06a195af41
commit 4220bba69b
11 changed files with 185 additions and 38 deletions
@@ -1,20 +1,22 @@
import { useEffect } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useLocation } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoadedState';
import { getCaptchaUrlByProvider } from '@/captcha/utils/getCaptchaUrlByProvider';
import { captchaState } from '@/client-config/states/captchaState';
import { CaptchaDriverType } from '~/generated/graphql';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath';
import { useCaptcha } from '@/client-config/hooks/useCaptcha';
import { captchaState } from '@/client-config/states/captchaState';
import { assertIsDefinedOrThrow } from 'twenty-shared/utils';
import { CaptchaDriverType } from '~/generated/graphql';
export const CaptchaProviderScriptLoaderEffect = () => {
const captcha = useRecoilValue(captchaState);
const [isCaptchaScriptLoaded, setIsCaptchaScriptLoaded] = useRecoilState(
const setIsCaptchaScriptLoaded = useSetRecoilState(
isCaptchaScriptLoadedState,
);
const { isCaptchaScriptLoaded, isCaptchaConfigured } = useCaptcha();
const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken();
const location = useLocation();
@@ -60,10 +62,12 @@ export const CaptchaProviderScriptLoaderEffect = () => {
]);
useEffect(() => {
if (isUndefinedOrNull(captcha?.provider) || !isCaptchaScriptLoaded) {
if (!isCaptchaConfigured || !isCaptchaScriptLoaded) {
return;
}
assertIsDefinedOrThrow(captcha);
let refreshInterval: NodeJS.Timeout;
switch (captcha.provider) {
@@ -81,7 +85,13 @@ export const CaptchaProviderScriptLoaderEffect = () => {
}
return () => clearInterval(refreshInterval);
}, [captcha?.provider, requestFreshCaptchaToken, isCaptchaScriptLoaded]);
}, [
captcha,
captcha?.provider,
isCaptchaConfigured,
isCaptchaScriptLoaded,
requestFreshCaptchaToken,
]);
return <></>;
};