allow logging into accounts that have tfa enabled
All checks were successful
Deploy / deploy (ubuntu-latest, 2.44.0) (push) Successful in 1m9s
All checks were successful
Deploy / deploy (ubuntu-latest, 2.44.0) (push) Successful in 1m9s
This commit is contained in:
parent
1facd632d4
commit
8520b77893
@ -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,16 +184,18 @@ 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 */}
|
||||||
<div className="relative">
|
{!borderCrossing && (
|
||||||
<EnvelopeIcon className="absolute left-2 top-[0.6rem] w-[1.15rem] h-[1.15rem]" />
|
<div className="relative">
|
||||||
<Input
|
<EnvelopeIcon className="absolute left-2 top-[0.6rem] w-[1.15rem] h-[1.15rem]" />
|
||||||
className="pl-8 rounded-lg"
|
<Input
|
||||||
type="email"
|
className="pl-8 rounded-lg"
|
||||||
placeholder="bob@example.com"
|
type="email"
|
||||||
disabled={stage !== "email"}
|
placeholder="bob@example.com"
|
||||||
{...register("email")}
|
disabled={stage !== "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),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user