diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index ef22a378d..6d5adb414 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -5,7 +5,6 @@ import { } from "@dokploy/server"; import { validateRequest } from "@dokploy/server/lib/auth"; import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema"; -import { REGEXP_ONLY_DIGITS } from "input-otp"; import { Fingerprint } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; @@ -38,14 +37,9 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { - InputOTP, - InputOTPGroup, - InputOTPSeparator, - InputOTPSlot, -} from "@/components/ui/input-otp"; import { Label } from "@/components/ui/label"; import { authClient } from "@/lib/auth-client"; +import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { generateServerSideHelper } from "@/utils/create-server-helpers"; @@ -77,6 +71,9 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) { const [isTwoFactor, setIsTwoFactor] = useState(false); const [error, setError] = useState(null); const [twoFactorCode, setTwoFactorCode] = useState(""); + const [twoFactorCaret, setTwoFactorCaret] = useState(0); + // Clamped so a caret left over from before the code was cleared can't point past it. + const twoFactorCursor = Math.min(twoFactorCaret, twoFactorCode.length); const [isBackupCodeModalOpen, setIsBackupCodeModalOpen] = useState(false); const [backupCode, setBackupCode] = useState(""); const loginForm = useForm({ @@ -183,12 +180,15 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) { toast.error("Please enter a valid 6-digit code"); return; } + await verifyTwoFactorCode(twoFactorCode); + }; + + const verifyTwoFactorCode = async (code: string) => { + if (isTwoFactorLoading) return; setIsTwoFactorLoading(true); try { - const { error } = await authClient.twoFactor.verifyTotp({ - code: twoFactorCode.replace(/\s/g, ""), - }); + const { error } = await authClient.twoFactor.verifyTotp({ code }); if (error) { toast.error(error.message); @@ -346,27 +346,44 @@ export default function Home({ IS_CLOUD, enforceSSO }: Props) { >
- - - - - - - - - - - - - + {/* Visible input so password managers can autofill it */} +
+ { + const code = e.target.value.replace(/\D/g, ""); + setTwoFactorCode(code); + setTwoFactorCaret(e.target.selectionStart ?? code.length); + if (code.length === 6) verifyTwoFactorCode(code); + }} + onSelect={(e) => + setTwoFactorCaret(e.currentTarget.selectionStart ?? 0) + } + autoComplete="one-time-code" + inputMode="numeric" + maxLength={6} + readOnly={isTwoFactorLoading} + autoFocus + className={cn( + "h-8 border-0 py-0 pr-0 pl-[calc((2rem_-_1ch)/2)] font-mono text-base tracking-[calc(2rem_-_1ch)] focus-visible:ring-0 md:text-base dark:bg-transparent", + twoFactorCursor === 6 && "caret-transparent", + )} + /> +
+ {[0, 1, 2, 3, 4, 5].map((slot) => ( +
+ ))} +
+
Enter the 6-digit code from your authenticator app