mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-24 01:12:27 +00:00
Merge pull request #5735 from imrja8/fix/2fa-password-manager-autofill
Fix/2fa password manager autofill
This commit is contained in:
@@ -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<string | null>(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<LoginForm>({
|
||||
@@ -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) {
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="totp-code">2FA Code</Label>
|
||||
<InputOTP
|
||||
id="totp-code"
|
||||
name="totp"
|
||||
value={twoFactorCode}
|
||||
onChange={setTwoFactorCode}
|
||||
maxLength={6}
|
||||
pattern={REGEXP_ONLY_DIGITS}
|
||||
autoFocus
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} />
|
||||
<InputOTPSlot index={1} />
|
||||
<InputOTPSlot index={2} />
|
||||
</InputOTPGroup>
|
||||
<InputOTPSeparator />
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={3} />
|
||||
<InputOTPSlot index={4} />
|
||||
<InputOTPSlot index={5} />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
{/* Visible input so password managers can autofill it */}
|
||||
<div className="group relative isolate w-56">
|
||||
<Input
|
||||
id="totp-code"
|
||||
name="totp"
|
||||
value={twoFactorCode}
|
||||
onChange={(e) => {
|
||||
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",
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-y-0 left-0 -z-10 flex"
|
||||
>
|
||||
{[0, 1, 2, 3, 4, 5].map((slot) => (
|
||||
<div
|
||||
key={slot}
|
||||
data-active={slot === Math.min(twoFactorCursor, 5)}
|
||||
className="relative size-8 border-y border-r border-input first:rounded-l-lg first:border-l last:rounded-r-lg dark:bg-input/30 group-focus-within:data-[active=true]:z-10 group-focus-within:data-[active=true]:border-ring group-focus-within:data-[active=true]:ring-3 group-focus-within:data-[active=true]:ring-ring/50"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<CardDescription>
|
||||
Enter the 6-digit code from your authenticator app
|
||||
</CardDescription>
|
||||
|
||||
Reference in New Issue
Block a user