diff --git a/packages/twenty-front/src/modules/apollo/components/ApolloProvider.tsx b/packages/twenty-front/src/modules/apollo/components/ApolloProvider.tsx index c0d771c61..0de8afedd 100644 --- a/packages/twenty-front/src/modules/apollo/components/ApolloProvider.tsx +++ b/packages/twenty-front/src/modules/apollo/components/ApolloProvider.tsx @@ -1,10 +1,17 @@ import { ApolloProvider as ApolloProviderBase } from '@apollo/client'; import { useApolloFactory } from '@/apollo/hooks/useApolloFactory'; +import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken'; +import { createCaptchaRefreshLink } from '@/apollo/utils/captchaRefreshLink'; export const ApolloProvider = ({ children }: React.PropsWithChildren) => { + const { requestFreshCaptchaToken } = useRequestFreshCaptchaToken(); + + const captchaRefreshLink = createCaptchaRefreshLink(requestFreshCaptchaToken); + const apolloClient = useApolloFactory({ connectToDevTools: true, + extraLinks: [captchaRefreshLink], }); // This will attach the right apollo client to Apollo Dev Tools diff --git a/packages/twenty-front/src/modules/apollo/utils/captchaRefreshLink.ts b/packages/twenty-front/src/modules/apollo/utils/captchaRefreshLink.ts new file mode 100644 index 000000000..3f9a7a416 --- /dev/null +++ b/packages/twenty-front/src/modules/apollo/utils/captchaRefreshLink.ts @@ -0,0 +1,19 @@ +import { ApolloLink } from '@apollo/client'; + +export const createCaptchaRefreshLink = ( + requestFreshCaptchaToken: () => void, +) => { + return new ApolloLink((operation, forward) => { + const { variables } = operation; + + const hasCaptchaToken = variables && 'captchaToken' in variables; + + return forward(operation).map((response) => { + if (hasCaptchaToken) { + requestFreshCaptchaToken(); + } + + return response; + }); + }); +}; diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts index 15b293652..9ad2e80b1 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts @@ -501,7 +501,8 @@ export const useAuth = () => { const handleSignOut = useCallback(async () => { await clearSession(); - }, [clearSession]); + await requestFreshCaptchaToken(); + }, [clearSession, requestFreshCaptchaToken]); const handleCredentialsSignUpInWorkspace = useCallback( async ({ @@ -519,60 +520,55 @@ export const useAuth = () => { captchaToken?: string; verifyEmailNextPath?: string; }) => { - try { - const signUpInWorkspaceResult = await signUpInWorkspace({ - variables: { - email, - password, - workspaceInviteHash, - workspacePersonalInviteToken, - captchaToken, - locale: i18n.locale ?? 'en', - ...(workspacePublicData?.id - ? { workspaceId: workspacePublicData.id } - : {}), - verifyEmailNextPath, - }, - }); + const signUpInWorkspaceResult = await signUpInWorkspace({ + variables: { + email, + password, + workspaceInviteHash, + workspacePersonalInviteToken, + captchaToken, + locale: i18n.locale ?? 'en', + ...(workspacePublicData?.id + ? { workspaceId: workspacePublicData.id } + : {}), + verifyEmailNextPath, + }, + }); - if (isDefined(signUpInWorkspaceResult.errors)) { - throw signUpInWorkspaceResult.errors; - } - - if (!signUpInWorkspaceResult.data?.signUpInWorkspace) { - throw new Error('No login token'); - } - - if (isEmailVerificationRequired) { - setSearchParams({ email }); - setSignInUpStep(SignInUpStep.EmailVerification); - return null; - } - - if (isMultiWorkspaceEnabled) { - return await redirectToWorkspaceDomain( - getWorkspaceUrl( - signUpInWorkspaceResult.data.signUpInWorkspace.workspace - .workspaceUrls, - ), - isEmailVerificationRequired ? AppPath.SignInUp : AppPath.Verify, - { - ...(!isEmailVerificationRequired && { - loginToken: - signUpInWorkspaceResult.data.signUpInWorkspace.loginToken - .token, - }), - email, - }, - ); - } - - await handleGetAuthTokensFromLoginToken( - signUpInWorkspaceResult.data?.signUpInWorkspace.loginToken.token, - ); - } finally { - requestFreshCaptchaToken(); + if (isDefined(signUpInWorkspaceResult.errors)) { + throw signUpInWorkspaceResult.errors; } + + if (!signUpInWorkspaceResult.data?.signUpInWorkspace) { + throw new Error('No login token'); + } + + if (isEmailVerificationRequired) { + setSearchParams({ email }); + setSignInUpStep(SignInUpStep.EmailVerification); + return null; + } + + if (isMultiWorkspaceEnabled) { + return await redirectToWorkspaceDomain( + getWorkspaceUrl( + signUpInWorkspaceResult.data.signUpInWorkspace.workspace + .workspaceUrls, + ), + isEmailVerificationRequired ? AppPath.SignInUp : AppPath.Verify, + { + ...(!isEmailVerificationRequired && { + loginToken: + signUpInWorkspaceResult.data.signUpInWorkspace.loginToken.token, + }), + email, + }, + ); + } + + await handleGetAuthTokensFromLoginToken( + signUpInWorkspaceResult.data?.signUpInWorkspace.loginToken.token, + ); }, [ signUpInWorkspace, @@ -583,7 +579,6 @@ export const useAuth = () => { setSearchParams, isEmailVerificationRequired, redirectToWorkspaceDomain, - requestFreshCaptchaToken, ], ); diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts index 8ff136e91..9a70346e4 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUp.ts @@ -10,7 +10,6 @@ import { } from '@/auth/states/signInUpStepState'; import { SignInUpMode } from '@/auth/types/signInUpMode'; import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; -import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken'; import { useBuildSearchParamsFromUrlSyncedStates } from '@/domain-manager/hooks/useBuildSearchParamsFromUrlSyncedStates'; import { AppPath } from '@/types/AppPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; @@ -47,16 +46,14 @@ export const useSignInUp = (form: UseFormReturn