Building a Two-Factor Authentication Form in React Using SMS Verification

Aron Schüler Published


Intro

In today’s web projects, securing user accounts is more important than ever. Simple passwords are using their value through dataleaks, and most users still reuse passwords. Two-Factor Authentication (2FA) adds an extra layer of security by requiring users to verify their identity through a second form of authentication, typically a code sent via SMS. This guide will walk you through building a 2FA form in React using react-hook-form. Let’s get started!

Step-by-Step Guide

Step 1: Setting Up Your React Project (Vite)

  • First, we will set up a new React project to implement the form. We will use Vite. You can do this with the npx create-vite command and the react-ts template:

    npx create-vite 2fa-sms-react --template react-ts
    cd 2fa-sms-react
  • Once your project is set up, open it in your code editor.

Step 2: Installing Required Dependencies

Managing form state and validation with React’s useState and useEffect hooks can be cumbersome. Instead, we’ll use react-hook-form to simplify form handling and validation.

  • Install react-hook-form to manage your form state:
    npm install react-hook-form

Step 3: Creating the 2FA Form Component

Next, we will setup a component that holds our two 2FA forms. We need one form to input the phone number that will receive the SMS code and another form to input the verification code. We will handle the form submission logic in this component with react-hook-form and utilize one of my favorite features: the autoComplete="one-time-code" attribute.

  • Create a new component named TwoFactorAuthForm.js in the src directory.

  • In this component, we’ll create a form with an input for the phone number and another form + input for the verification code using react-hook-form.

  • The autoComplete="one-time-code" attribute on the verification code input tells the browser to autofill the code if it detects a one-time code SMS. Very handy if you’re using a mobile device, I appreciate this feature a lot.

    import { useState } from 'react';
    import { useForm } from 'react-hook-form';
     
    export const TwoFactorAuthInput = () => {
      type TwoFactorAuthForm = {
        phoneNumber: string;
        verificationCode: number;
      };
     
      const [hasCodeIncoming, setHasCodeIncoming] = useState(false);
     
      const {
        register,
        handleSubmit,
        reset,
        formState: { errors },
        watch,
        setError,
      } = useForm<TwoFactorAuthForm>();
     
      const phoneNumber = watch('phoneNumber');
     
      const sendCode = () => {
        //
        setHasCodeIncoming(true);
        // Logic to send the SMS verification code
        console.log(`Sending verification code to ${phoneNumber}`);
        // reset the form after sending the code
        reset({ phoneNumber });
      };
     
      const onSubmit = (data: TwoFactorAuthForm) => {
        // Logic to verify the SMS verification code
        console.log(`Verifying code: ${data.verificationCode}`);
        // Handle a code failure, e.g.
        if (data.verificationCode !== 123456) {
          setError('verificationCode', {
            message: 'This code is not valid. Try 123456!',
          });
        }
      };
     
      return (
        <div>
          <h2>Two-Factor Authentication</h2>
          <form
            onSubmit={handleSubmit(onSubmit)}
          >
            <div>
              <label>Phone Number:</label>
              <input
                type="tel"
                {...register('phoneNumber', { required: true })}
                required
              />
              <button type="button" onClick={sendCode}>
                Send Code
              </button>
            </div>
            <div>
              <label>Verification Code:</label>
              <input
                disabled={!hasCodeIncoming}
                type="text"
                {...register('verificationCode', { required: true })}
                autoComplete="one-time-code"
                required
              />
              {errors['verificationCode'] && (
                <small style={{ color: 'red' }}>
                  {errors['verificationCode'].message ??
                    'Something is wrong with this code.'}
                </small>
              )}
              <button disabled={!hasCodeIncoming} type="submit">
                Verify Code
              </button>
            </div>
          </form>
        </div>
      );
    };

You can find an extended version of this component in the StackBlitz example.

Step 4: Integrating SMS API for Sending Codes

To send SMS codes, you need a service like Twilio and an API endpoint on your backend to handle the SMS sending logic. For this guide, we will focus on the frontend, but the backend integration should look like this:

  • Create an API endpoint on your backend to send the SMS verification code.
  • Store the verification code on the backend (e.g., in a database or in-memory store) and check the code entered by the user against the stored code.
  • Update onVerifyCode to send the verification code to the backend for verification.

Conclusion

Building a 2FA form in React using SMS verification significantly enhances your application’s security. Utilizing react-hook-form simplifies form handling and state management, and the autoComplete="one-time-code" attribute streamlines the user experience by allowing the browser to auto-fill the verification code. By following this guide, you’ve created a more secure and user-friendly authentication process. Feel free to ask questions or share your experiences in the comments below. Happy coding!


Related Posts

Find posts on similar topics:


Support me via buymeacoffee

If I helped you solve a problem or you just like my content, consider supporting me by buying me a coffee.

Buy me a coffee

Comments