Captcha Refactoring (#397)
* refactor: replace useCaptchaToken with useCaptcha hook and integrate CaptchaField across forms
This commit is contained in:
committed by
GitHub
parent
9eccb319af
commit
ea0c1dde80
@@ -23,7 +23,7 @@ import { Input } from '@kit/ui/input';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { useCaptchaToken } from '../captcha/client';
|
||||
import { useCaptcha } from '../captcha/client';
|
||||
import { useLastAuthMethod } from '../hooks/use-last-auth-method';
|
||||
import { TermsAndConditionsFormField } from './terms-and-conditions-form-field';
|
||||
|
||||
@@ -32,16 +32,18 @@ export function MagicLinkAuthContainer({
|
||||
shouldCreateUser,
|
||||
defaultValues,
|
||||
displayTermsCheckbox,
|
||||
captchaSiteKey,
|
||||
}: {
|
||||
redirectUrl: string;
|
||||
shouldCreateUser: boolean;
|
||||
displayTermsCheckbox?: boolean;
|
||||
captchaSiteKey?: string;
|
||||
|
||||
defaultValues?: {
|
||||
email: string;
|
||||
};
|
||||
}) {
|
||||
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
|
||||
const captcha = useCaptcha({ siteKey: captchaSiteKey });
|
||||
const { t } = useTranslation();
|
||||
const signInWithOtpMutation = useSignInWithOtp();
|
||||
const appEvents = useAppEvents();
|
||||
@@ -68,7 +70,7 @@ export function MagicLinkAuthContainer({
|
||||
email,
|
||||
options: {
|
||||
emailRedirectTo,
|
||||
captchaToken,
|
||||
captchaToken: captcha.token,
|
||||
shouldCreateUser,
|
||||
},
|
||||
});
|
||||
@@ -91,7 +93,7 @@ export function MagicLinkAuthContainer({
|
||||
error: t(`auth:errors.linkTitle`),
|
||||
});
|
||||
|
||||
resetCaptchaToken();
|
||||
captcha.reset();
|
||||
};
|
||||
|
||||
if (signInWithOtpMutation.data) {
|
||||
@@ -106,6 +108,8 @@ export function MagicLinkAuthContainer({
|
||||
<ErrorAlert />
|
||||
</If>
|
||||
|
||||
{captcha.field}
|
||||
|
||||
<FormField
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { Spinner } from '@kit/ui/spinner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { useCaptchaToken } from '../captcha/client';
|
||||
import { useCaptcha } from '../captcha/client';
|
||||
import { useLastAuthMethod } from '../hooks/use-last-auth-method';
|
||||
import { AuthErrorAlert } from './auth-error-alert';
|
||||
|
||||
@@ -36,6 +36,7 @@ const OtpSchema = z.object({ token: z.string().min(6).max(6) });
|
||||
|
||||
type OtpSignInContainerProps = {
|
||||
shouldCreateUser: boolean;
|
||||
captchaSiteKey?: string;
|
||||
};
|
||||
|
||||
export function OtpSignInContainer(props: OtpSignInContainerProps) {
|
||||
@@ -88,6 +89,7 @@ export function OtpSignInContainer(props: OtpSignInContainerProps) {
|
||||
return (
|
||||
<OtpEmailForm
|
||||
shouldCreateUser={shouldCreateUser}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
onSendOtp={(email) => {
|
||||
otpForm.setValue('email', email, {
|
||||
shouldValidate: true,
|
||||
@@ -174,12 +176,14 @@ export function OtpSignInContainer(props: OtpSignInContainerProps) {
|
||||
|
||||
function OtpEmailForm({
|
||||
shouldCreateUser,
|
||||
captchaSiteKey,
|
||||
onSendOtp,
|
||||
}: {
|
||||
shouldCreateUser: boolean;
|
||||
captchaSiteKey?: string;
|
||||
onSendOtp: (email: string) => void;
|
||||
}) {
|
||||
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
|
||||
const captcha = useCaptcha({ siteKey: captchaSiteKey });
|
||||
const signInMutation = useSignInWithOtp();
|
||||
|
||||
const emailForm = useForm({
|
||||
@@ -190,10 +194,10 @@ function OtpEmailForm({
|
||||
const handleSendOtp = async ({ email }: z.infer<typeof EmailSchema>) => {
|
||||
await signInMutation.mutateAsync({
|
||||
email,
|
||||
options: { captchaToken, shouldCreateUser },
|
||||
options: { captchaToken: captcha.token, shouldCreateUser },
|
||||
});
|
||||
|
||||
resetCaptchaToken();
|
||||
captcha.reset();
|
||||
onSendOtp(email);
|
||||
};
|
||||
|
||||
@@ -205,6 +209,8 @@ function OtpEmailForm({
|
||||
>
|
||||
<AuthErrorAlert error={signInMutation.error} />
|
||||
|
||||
{captcha.field}
|
||||
|
||||
<FormField
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
|
||||
@@ -20,7 +20,7 @@ import { If } from '@kit/ui/if';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { useCaptchaToken } from '../captcha/client';
|
||||
import { useCaptcha } from '../captcha/client';
|
||||
import { AuthErrorAlert } from './auth-error-alert';
|
||||
|
||||
const PasswordResetSchema = z.object({
|
||||
@@ -29,10 +29,11 @@ const PasswordResetSchema = z.object({
|
||||
|
||||
export function PasswordResetRequestContainer(params: {
|
||||
redirectPath: string;
|
||||
captchaSiteKey?: string;
|
||||
}) {
|
||||
const { t } = useTranslation('auth');
|
||||
const resetPasswordMutation = useRequestResetPassword();
|
||||
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
|
||||
const captcha = useCaptcha({ siteKey: params.captchaSiteKey });
|
||||
|
||||
const error = resetPasswordMutation.error;
|
||||
const success = resetPasswordMutation.data;
|
||||
@@ -67,10 +68,10 @@ export function PasswordResetRequestContainer(params: {
|
||||
.mutateAsync({
|
||||
email,
|
||||
redirectTo,
|
||||
captchaToken,
|
||||
captchaToken: captcha.token,
|
||||
})
|
||||
.catch(() => {
|
||||
resetCaptchaToken();
|
||||
captcha.reset();
|
||||
});
|
||||
})}
|
||||
className={'w-full'}
|
||||
@@ -78,6 +79,8 @@ export function PasswordResetRequestContainer(params: {
|
||||
<div className={'flex flex-col gap-y-4'}>
|
||||
<AuthErrorAlert error={error} />
|
||||
|
||||
{captcha.field}
|
||||
|
||||
<FormField
|
||||
name={'email'}
|
||||
render={({ field }) => (
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { z } from 'zod';
|
||||
|
||||
import { useSignInWithEmailPassword } from '@kit/supabase/hooks/use-sign-in-with-email-password';
|
||||
|
||||
import { useCaptchaToken } from '../captcha/client';
|
||||
import { useCaptcha } from '../captcha/client';
|
||||
import { useLastAuthMethod } from '../hooks/use-last-auth-method';
|
||||
import type { PasswordSignInSchema } from '../schemas/password-sign-in.schema';
|
||||
import { AuthErrorAlert } from './auth-error-alert';
|
||||
@@ -14,10 +14,12 @@ import { PasswordSignInForm } from './password-sign-in-form';
|
||||
|
||||
export function PasswordSignInContainer({
|
||||
onSignIn,
|
||||
captchaSiteKey,
|
||||
}: {
|
||||
onSignIn?: (userId?: string) => unknown;
|
||||
captchaSiteKey?: string;
|
||||
}) {
|
||||
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
|
||||
const captcha = useCaptcha({ siteKey: captchaSiteKey });
|
||||
const signInMutation = useSignInWithEmailPassword();
|
||||
const { recordAuthMethod } = useLastAuthMethod();
|
||||
const isLoading = signInMutation.isPending;
|
||||
@@ -28,7 +30,7 @@ export function PasswordSignInContainer({
|
||||
try {
|
||||
const data = await signInMutation.mutateAsync({
|
||||
...credentials,
|
||||
options: { captchaToken },
|
||||
options: { captchaToken: captcha.token },
|
||||
});
|
||||
|
||||
// Record successful password sign-in
|
||||
@@ -42,22 +44,18 @@ export function PasswordSignInContainer({
|
||||
} catch {
|
||||
// wrong credentials, do nothing
|
||||
} finally {
|
||||
resetCaptchaToken();
|
||||
captcha.reset();
|
||||
}
|
||||
},
|
||||
[
|
||||
captchaToken,
|
||||
onSignIn,
|
||||
resetCaptchaToken,
|
||||
signInMutation,
|
||||
recordAuthMethod,
|
||||
],
|
||||
[captcha, onSignIn, signInMutation, recordAuthMethod],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthErrorAlert error={signInMutation.error} />
|
||||
|
||||
{captcha.field}
|
||||
|
||||
<PasswordSignInForm
|
||||
onSubmit={onSubmit}
|
||||
loading={isLoading}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { useCaptchaToken } from '../captcha/client';
|
||||
import { useCaptcha } from '../captcha/client';
|
||||
import { usePasswordSignUpFlow } from '../hooks/use-sign-up-flow';
|
||||
import { AuthErrorAlert } from './auth-error-alert';
|
||||
import { PasswordSignUpForm } from './password-sign-up-form';
|
||||
@@ -18,6 +18,7 @@ interface EmailPasswordSignUpContainerProps {
|
||||
};
|
||||
onSignUp?: (userId?: string) => unknown;
|
||||
emailRedirectTo: string;
|
||||
captchaSiteKey?: string;
|
||||
}
|
||||
|
||||
export function EmailPasswordSignUpContainer({
|
||||
@@ -25,8 +26,9 @@ export function EmailPasswordSignUpContainer({
|
||||
onSignUp,
|
||||
emailRedirectTo,
|
||||
displayTermsCheckbox,
|
||||
captchaSiteKey,
|
||||
}: EmailPasswordSignUpContainerProps) {
|
||||
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
|
||||
const captcha = useCaptcha({ siteKey: captchaSiteKey });
|
||||
|
||||
const {
|
||||
signUp: onSignupRequested,
|
||||
@@ -36,8 +38,8 @@ export function EmailPasswordSignUpContainer({
|
||||
} = usePasswordSignUpFlow({
|
||||
emailRedirectTo,
|
||||
onSignUp,
|
||||
captchaToken,
|
||||
resetCaptchaToken,
|
||||
captchaToken: captcha.token,
|
||||
resetCaptchaToken: captcha.reset,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -49,6 +51,8 @@ export function EmailPasswordSignUpContainer({
|
||||
<If condition={!showVerifyEmailAlert}>
|
||||
<AuthErrorAlert error={error} />
|
||||
|
||||
{captcha.field}
|
||||
|
||||
<PasswordSignUpForm
|
||||
onSubmit={onSignupRequested}
|
||||
loading={loading}
|
||||
|
||||
@@ -18,10 +18,14 @@ import {
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { useCaptchaToken } from '../captcha/client';
|
||||
import { useCaptcha } from '../captcha/client';
|
||||
|
||||
export function ResendAuthLinkForm(props: { redirectPath?: string }) {
|
||||
const resendLink = useResendLink();
|
||||
export function ResendAuthLinkForm(props: {
|
||||
redirectPath?: string;
|
||||
captchaSiteKey?: string;
|
||||
}) {
|
||||
const captcha = useCaptcha({ siteKey: props.captchaSiteKey });
|
||||
const resendLink = useResendLink(captcha.token);
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(z.object({ email: z.string().email() })),
|
||||
@@ -52,12 +56,20 @@ export function ResendAuthLinkForm(props: { redirectPath?: string }) {
|
||||
<form
|
||||
className={'flex flex-col space-y-2'}
|
||||
onSubmit={form.handleSubmit((data) => {
|
||||
return resendLink.mutate({
|
||||
const promise = resendLink.mutateAsync({
|
||||
email: data.email,
|
||||
redirectPath: props.redirectPath,
|
||||
});
|
||||
|
||||
promise.finally(() => {
|
||||
captcha.reset();
|
||||
});
|
||||
|
||||
return promise;
|
||||
})}
|
||||
>
|
||||
{captcha.field}
|
||||
|
||||
<FormField
|
||||
render={({ field }) => {
|
||||
return (
|
||||
@@ -83,9 +95,8 @@ export function ResendAuthLinkForm(props: { redirectPath?: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function useResendLink() {
|
||||
function useResendLink(captchaToken: string) {
|
||||
const supabase = useSupabase();
|
||||
const { captchaToken } = useCaptchaToken();
|
||||
|
||||
const mutationFn = async (props: {
|
||||
email: string;
|
||||
|
||||
@@ -30,6 +30,8 @@ export function SignInMethodsContainer(props: {
|
||||
otp: boolean;
|
||||
oAuth: Provider[];
|
||||
};
|
||||
|
||||
captchaSiteKey?: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
@@ -48,18 +50,25 @@ export function SignInMethodsContainer(props: {
|
||||
<LastAuthMethodHint />
|
||||
|
||||
<If condition={props.providers.password}>
|
||||
<PasswordSignInContainer onSignIn={onSignIn} />
|
||||
<PasswordSignInContainer
|
||||
onSignIn={onSignIn}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={props.providers.magicLink}>
|
||||
<MagicLinkAuthContainer
|
||||
redirectUrl={redirectUrl}
|
||||
shouldCreateUser={false}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={props.providers.otp}>
|
||||
<OtpSignInContainer shouldCreateUser={false} />
|
||||
<OtpSignInContainer
|
||||
shouldCreateUser={false}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={props.providers.oAuth.length}>
|
||||
|
||||
@@ -27,6 +27,7 @@ export function SignUpMethodsContainer(props: {
|
||||
};
|
||||
|
||||
displayTermsCheckbox?: boolean;
|
||||
captchaSiteKey?: string;
|
||||
}) {
|
||||
const redirectUrl = getCallbackUrl(props);
|
||||
const defaultValues = getDefaultValues();
|
||||
@@ -41,11 +42,15 @@ export function SignUpMethodsContainer(props: {
|
||||
emailRedirectTo={redirectUrl}
|
||||
defaultValues={defaultValues}
|
||||
displayTermsCheckbox={props.displayTermsCheckbox}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={props.providers.otp}>
|
||||
<OtpSignInContainer shouldCreateUser={true} />
|
||||
<OtpSignInContainer
|
||||
shouldCreateUser={true}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={props.providers.magicLink}>
|
||||
@@ -54,6 +59,7 @@ export function SignUpMethodsContainer(props: {
|
||||
shouldCreateUser={true}
|
||||
defaultValues={defaultValues}
|
||||
displayTermsCheckbox={props.displayTermsCheckbox}
|
||||
captchaSiteKey={props.captchaSiteKey}
|
||||
/>
|
||||
</If>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user