Skip to content

Commit 1117f35

Browse files
refactor: use plain input for totp input to fix autofill issues (#790)
* fix(ui): allow pw manager extensions to autofill totp * chore: small ui fixes * fix: prevent double totp submissions --------- Co-authored-by: Scott McKendry <me@scottmckendry.tech>
1 parent cc94294 commit 1117f35

5 files changed

Lines changed: 21 additions & 106 deletions

File tree

frontend/bun.lock

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
"i18next": "^26.0.3",
2626
"i18next-browser-languagedetector": "^8.2.1",
2727
"i18next-resources-to-backend": "^1.2.1",
28-
"input-otp": "^1.4.2",
2928
"lucide-react": "^1.7.0",
3029
"next-themes": "^0.4.6",
3130
"radix-ui": "^1.4.3",

frontend/src/components/auth/totp-form.tsx

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { Form, FormControl, FormField, FormItem } from "../ui/form";
2-
import {
3-
InputOTP,
4-
InputOTPGroup,
5-
InputOTPSeparator,
6-
InputOTPSlot,
7-
} from "../ui/input-otp";
2+
import { Input } from "../ui/input";
83
import { zodResolver } from "@hookform/resolvers/zod";
94
import { useForm } from "react-hook-form";
105
import { totpSchema, TotpSchema } from "@/schemas/totp-schema";
116
import { useTranslation } from "react-i18next";
7+
import { useRef } from "react";
128
import z from "zod";
139

1410
interface Props {
@@ -19,6 +15,7 @@ interface Props {
1915
export const TotpForm = (props: Props) => {
2016
const { formId, onSubmit } = props;
2117
const { t } = useTranslation();
18+
const autoSubmittedRef = useRef(false);
2219

2320
z.config({
2421
customError: (iss) =>
@@ -29,14 +26,19 @@ export const TotpForm = (props: Props) => {
2926
resolver: zodResolver(totpSchema),
3027
});
3128

32-
const handleChange = (value: string) => {
33-
form.setValue("code", value, { shouldDirty: true, shouldValidate: true });
34-
35-
if (value.length === 6) {
36-
onSubmit({ code: value });
29+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
30+
const value = e.target.value.replace(/\D/g, "").slice(0, 6);
31+
form.setValue("code", value, { shouldDirty: true, shouldValidate: false });
32+
if (value.length === 6 && !autoSubmittedRef.current) {
33+
autoSubmittedRef.current = true;
34+
form.handleSubmit(onSubmit)();
35+
return;
3736
}
37+
autoSubmittedRef.current = false;
3838
};
3939

40+
// Note: This is not the best UX, ideally we would want https://github.com/guilhermerodz/input-otp
41+
// but some password managers cannot autofill the inputs (see #92) so, simple input it is
4042
return (
4143
<Form {...form}>
4244
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
@@ -46,25 +48,17 @@ export const TotpForm = (props: Props) => {
4648
render={({ field }) => (
4749
<FormItem>
4850
<FormControl>
49-
<InputOTP
50-
maxLength={6}
51+
<Input
5152
{...field}
53+
type="text"
54+
inputMode="numeric"
5255
autoComplete="one-time-code"
5356
autoFocus
57+
maxLength={6}
58+
placeholder="XXXXXX"
5459
onChange={handleChange}
55-
>
56-
<InputOTPGroup>
57-
<InputOTPSlot index={0} />
58-
<InputOTPSlot index={1} />
59-
<InputOTPSlot index={2} />
60-
</InputOTPGroup>
61-
<InputOTPSeparator />
62-
<InputOTPGroup>
63-
<InputOTPSlot index={3} />
64-
<InputOTPSlot index={4} />
65-
<InputOTPSlot index={5} />
66-
</InputOTPGroup>
67-
</InputOTP>
60+
className="text-center"
61+
/>
6862
</FormControl>
6963
</FormItem>
7064
)}

frontend/src/components/ui/input-otp.tsx

Lines changed: 0 additions & 75 deletions
This file was deleted.

frontend/src/pages/totp-page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export const TotpPage = () => {
7474
<CardTitle className="text-xl">{t("totpTitle")}</CardTitle>
7575
<CardDescription>{t("totpSubtitle")}</CardDescription>
7676
</CardHeader>
77-
<CardContent className="flex flex-col items-center">
77+
<CardContent>
7878
<TotpForm
7979
formId={formId}
8080
onSubmit={(values) => totpMutation.mutate(values)}

0 commit comments

Comments
 (0)