allow logging into accounts that have tfa enabled
All checks were successful
Deploy / deploy (ubuntu-latest, 2.44.0) (push) Successful in 1m9s

This commit is contained in:
Braydon 2024-09-19 08:15:23 -04:00
parent 1facd632d4
commit 8520b77893
2 changed files with 50 additions and 13 deletions

@ -24,6 +24,13 @@ import { UserAuthResponse } from "@/app/types/user/response/user-auth-response";
import { hasFlag } from "@/lib/user"; import { hasFlag } from "@/lib/user";
import { UserFlag } from "@/app/types/user/user-flag"; import { UserFlag } from "@/app/types/user/user-flag";
import { User } from "@/app/types/user/user"; import { User } from "@/app/types/user/user";
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { cn } from "@/lib/utils";
/** /**
* Define the form schemas for the various stages. * Define the form schemas for the various stages.
@ -60,6 +67,7 @@ const RegisterSchema = z
const LoginSchema = z.object({ const LoginSchema = z.object({
email: buildEmailInput(true), email: buildEmailInput(true),
password: z.string(), password: z.string(),
pin: z.string().optional(),
}); });
/** /**
@ -81,6 +89,10 @@ const AuthForm = (): ReactElement => {
const [captchaResponse, setCaptchaResponse] = useState<string | undefined>( const [captchaResponse, setCaptchaResponse] = useState<string | undefined>(
undefined undefined
); );
const [borderCrossing, setBorderCrossing] = useState<boolean>(false);
const [tfaPin, setTfaPin] = useState<string | undefined>();
const [error, setError] = useState<string | undefined>(undefined); const [error, setError] = useState<string | undefined>(undefined);
const turnstile: TurnstileObject = useTurnstile(); const turnstile: TurnstileObject = useTurnstile();
const cookies: Cookies = useCookies(); const cookies: Cookies = useCookies();
@ -133,8 +145,16 @@ const AuthForm = (): ReactElement => {
email, email,
password, password,
captchaResponse, captchaResponse,
tfaPin: tfaPin ?? "",
}, },
}); });
// Handle two-factor auth
if (error?.message === "BORDER_CROSSING") {
setBorderCrossing(true);
setLoading(false);
return;
}
setError(error?.message ?? undefined); setError(error?.message ?? undefined);
// Reset the captcha if auth fails // Reset the captcha if auth fails
@ -164,6 +184,7 @@ const AuthForm = (): ReactElement => {
return ( return (
<form className="flex flex-col gap-2" onSubmit={handleSubmit(onSubmit)}> <form className="flex flex-col gap-2" onSubmit={handleSubmit(onSubmit)}>
{/* Email Address */} {/* Email Address */}
{!borderCrossing && (
<div className="relative"> <div className="relative">
<EnvelopeIcon className="absolute left-2 top-[0.6rem] w-[1.15rem] h-[1.15rem]" /> <EnvelopeIcon className="absolute left-2 top-[0.6rem] w-[1.15rem] h-[1.15rem]" />
<Input <Input
@ -174,6 +195,7 @@ const AuthForm = (): ReactElement => {
{...register("email")} {...register("email")}
/> />
</div> </div>
)}
{/* Username */} {/* Username */}
{stage === "register" && ( {stage === "register" && (
@ -195,7 +217,7 @@ const AuthForm = (): ReactElement => {
)} )}
{/* Password */} {/* Password */}
{stage !== "email" && ( {stage !== "email" && !borderCrossing && (
<motion.div <motion.div
key="password" key="password"
className="relative" className="relative"
@ -214,6 +236,23 @@ const AuthForm = (): ReactElement => {
</motion.div> </motion.div>
)} )}
{/* TFA Pin */}
<div className={cn("mx-auto", !borderCrossing && "hidden")}>
<InputOTP maxLength={6} value={tfaPin} onChange={setTfaPin}>
<InputOTPGroup>
{[0, 1, 2].map((index: number) => (
<InputOTPSlot key={index} index={index} />
))}
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
{[3, 4, 5].map((index: number) => (
<InputOTPSlot key={index} index={index} />
))}
</InputOTPGroup>
</InputOTP>
</div>
{/* Password Confirmation */} {/* Password Confirmation */}
{stage === "register" && ( {stage === "register" && (
<motion.div <motion.div

@ -45,9 +45,7 @@ const TfaSetupForm = ({
// Build the form // Build the form
const { const {
register,
handleSubmit, handleSubmit,
watch,
formState: { errors }, formState: { errors },
} = useForm({ } = useForm({
resolver: zodResolver(FormSchema), resolver: zodResolver(FormSchema),