@@ -61,61 +61,17 @@ export const Enable2FA = () => {
6161 const [ isDialogOpen , setIsDialogOpen ] = useState ( false ) ;
6262 const [ step , setStep ] = useState < "password" | "verify" > ( "password" ) ;
6363 const [ isPasswordLoading , setIsPasswordLoading ] = useState ( false ) ;
64+ const [ otpValue , setOtpValue ] = useState ( "" ) ;
6465
65- const handlePasswordSubmit = async ( formData : PasswordForm ) => {
66- setIsPasswordLoading ( true ) ;
67- try {
68- const { data : enableData , error } = await authClient . twoFactor . enable ( {
69- password : formData . password ,
70- issuer : formData . issuer ,
71- } ) ;
72-
73- if ( ! enableData ) {
74- throw new Error ( error ?. message || "Error enabling 2FA" ) ;
75- }
76-
77- if ( enableData . backupCodes ) {
78- setBackupCodes ( enableData . backupCodes ) ;
79- }
80-
81- if ( enableData . totpURI ) {
82- const qrCodeUrl = await QRCode . toDataURL ( enableData . totpURI ) ;
83-
84- setData ( {
85- qrCodeUrl,
86- secret : enableData . totpURI . split ( "secret=" ) [ 1 ] ?. split ( "&" ) [ 0 ] || "" ,
87- totpURI : enableData . totpURI ,
88- } ) ;
89-
90- setStep ( "verify" ) ;
91- toast . success ( "Scan the QR code with your authenticator app" ) ;
92- } else {
93- throw new Error ( "No TOTP URI received from server" ) ;
94- }
95- } catch ( error ) {
96- toast . error (
97- error instanceof Error ? error . message : "Error setting up 2FA" ,
98- ) ;
99- passwordForm . setError ( "password" , {
100- message :
101- error instanceof Error ? error . message : "Error setting up 2FA" ,
102- } ) ;
103- } finally {
104- setIsPasswordLoading ( false ) ;
105- }
106- } ;
107-
108- const handleVerifySubmit = async ( formData : PinForm ) => {
66+ const handleVerifySubmit = async ( e : React . FormEvent ) => {
67+ e . preventDefault ( ) ;
10968 try {
11069 const result = await authClient . twoFactor . verifyTotp ( {
111- code : formData . pin ,
70+ code : otpValue ,
11271 } ) ;
11372
11473 if ( result . error ) {
11574 if ( result . error . code === "INVALID_TWO_FACTOR_AUTHENTICATION" ) {
116- pinForm . setError ( "pin" , {
117- message : "Invalid code. Please try again." ,
118- } ) ;
11975 toast . error ( "Invalid verification code" ) ;
12076 return ;
12177 }
@@ -137,15 +93,11 @@ export const Enable2FA = () => {
13793 ? "Connection error. Please check your internet connection."
13894 : error . message ;
13995
140- pinForm . setError ( "pin" , {
141- message : errorMessage ,
142- } ) ;
14396 toast . error ( errorMessage ) ;
14497 } else {
145- pinForm . setError ( "pin ", {
146- message : " Error verifying code ",
98+ toast . error ( "Error verifying 2FA code ", {
99+ description : error instanceof Error ? error . message : "Unknown error ",
147100 } ) ;
148- toast . error ( "Error verifying 2FA code" ) ;
149101 }
150102 }
151103 } ;
@@ -169,10 +121,62 @@ export const Enable2FA = () => {
169121 setStep ( "password" ) ;
170122 setData ( null ) ;
171123 setBackupCodes ( [ ] ) ;
172- passwordForm . reset ( ) ;
173- pinForm . reset ( ) ;
124+ setOtpValue ( "" ) ;
125+ passwordForm . reset ( {
126+ password : "" ,
127+ issuer : "" ,
128+ } ) ;
174129 }
175- } , [ isDialogOpen , passwordForm , pinForm ] ) ;
130+ } , [ isDialogOpen , passwordForm ] ) ;
131+
132+ useEffect ( ( ) => {
133+ if ( step === "verify" ) {
134+ setOtpValue ( "" ) ;
135+ }
136+ } , [ step ] ) ;
137+
138+ const handlePasswordSubmit = async ( formData : PasswordForm ) => {
139+ setIsPasswordLoading ( true ) ;
140+ try {
141+ const { data : enableData , error } = await authClient . twoFactor . enable ( {
142+ password : formData . password ,
143+ issuer : formData . issuer ,
144+ } ) ;
145+
146+ if ( ! enableData ) {
147+ throw new Error ( error ?. message || "Error enabling 2FA" ) ;
148+ }
149+
150+ if ( enableData . backupCodes ) {
151+ setBackupCodes ( enableData . backupCodes ) ;
152+ }
153+
154+ if ( enableData . totpURI ) {
155+ const qrCodeUrl = await QRCode . toDataURL ( enableData . totpURI ) ;
156+
157+ setData ( {
158+ qrCodeUrl,
159+ secret : enableData . totpURI . split ( "secret=" ) [ 1 ] ?. split ( "&" ) [ 0 ] || "" ,
160+ totpURI : enableData . totpURI ,
161+ } ) ;
162+
163+ setStep ( "verify" ) ;
164+ toast . success ( "Scan the QR code with your authenticator app" ) ;
165+ } else {
166+ throw new Error ( "No TOTP URI received from server" ) ;
167+ }
168+ } catch ( error ) {
169+ toast . error (
170+ error instanceof Error ? error . message : "Error setting up 2FA" ,
171+ ) ;
172+ passwordForm . setError ( "password" , {
173+ message :
174+ error instanceof Error ? error . message : "Error setting up 2FA" ,
175+ } ) ;
176+ } finally {
177+ setIsPasswordLoading ( false ) ;
178+ }
179+ } ;
176180
177181 return (
178182 < Dialog open = { isDialogOpen } onOpenChange = { setIsDialogOpen } >
@@ -233,7 +237,8 @@ export const Enable2FA = () => {
233237 />
234238 </ FormControl >
235239 < FormDescription >
236- Enter your password to enable 2FA
240+ Use a custom issuer to identify the service you're
241+ authenticating with.
237242 </ FormDescription >
238243 < FormMessage />
239244 </ FormItem >
@@ -250,11 +255,7 @@ export const Enable2FA = () => {
250255 </ Form >
251256 ) : (
252257 < Form { ...pinForm } >
253- < form
254- id = "pin-form"
255- onSubmit = { pinForm . handleSubmit ( handleVerifySubmit ) }
256- className = "space-y-6"
257- >
258+ < form onSubmit = { handleVerifySubmit } className = "space-y-6" >
258259 < div className = "flex flex-col gap-6 justify-center items-center" >
259260 { data ?. qrCodeUrl ? (
260261 < >
@@ -306,36 +307,33 @@ export const Enable2FA = () => {
306307 ) }
307308 </ div >
308309
309- < FormField
310- control = { pinForm . control }
311- name = "pin"
312- render = { ( { field } ) => (
313- < FormItem className = "flex flex-col justify-center items-center" >
314- < FormLabel > Verification Code</ FormLabel >
315- < FormControl >
316- < InputOTP maxLength = { 6 } { ...field } >
317- < InputOTPGroup >
318- < InputOTPSlot index = { 0 } />
319- < InputOTPSlot index = { 1 } />
320- < InputOTPSlot index = { 2 } />
321- < InputOTPSlot index = { 3 } />
322- < InputOTPSlot index = { 4 } />
323- < InputOTPSlot index = { 5 } />
324- </ InputOTPGroup >
325- </ InputOTP >
326- </ FormControl >
327- < FormDescription >
328- Enter the 6-digit code from your authenticator app
329- </ FormDescription >
330- < FormMessage />
331- </ FormItem >
332- ) }
333- />
310+ < div className = "flex flex-col justify-center items-center" >
311+ < FormLabel > Verification Code</ FormLabel >
312+ < InputOTP
313+ maxLength = { 6 }
314+ value = { otpValue }
315+ onChange = { setOtpValue }
316+ autoComplete = "off"
317+ >
318+ < InputOTPGroup >
319+ < InputOTPSlot index = { 0 } />
320+ < InputOTPSlot index = { 1 } />
321+ < InputOTPSlot index = { 2 } />
322+ < InputOTPSlot index = { 3 } />
323+ < InputOTPSlot index = { 4 } />
324+ < InputOTPSlot index = { 5 } />
325+ </ InputOTPGroup >
326+ </ InputOTP >
327+ < FormDescription >
328+ Enter the 6-digit code from your authenticator app
329+ </ FormDescription >
330+ </ div >
334331
335332 < Button
336333 type = "submit"
337334 className = "w-full"
338335 isLoading = { isPasswordLoading }
336+ disabled = { otpValue . length !== 6 }
339337 >
340338 Enable 2FA
341339 </ Button >
0 commit comments