Skip to content
Alchemy Logo

Email OTP with Multi-Factor Authentication

This page documents Account Kit (v4) React. v5 (@alchemy/wallet-apis) is framework-agnostic and does not currently include a dedicated React package. Drop it into any framework with the state manager of your choice. See the Wallet APIs quickstart and the transaction guides.

This guide shows you how to implement Email OTP authentication when a user has multi-factor authentication (MFA) enabled.

When MFA is enabled, the authentication process requires two steps:

  1. Verify the user's email with a one-time password
  2. Verify the 6-digit code (TOTP) from their authenticator app

First, initiate the email OTP authentication process:

import React from "react";
import { useAuthenticate } from "@account-kit/react";
 
// Inside your component
const { authenticate } = useAuthenticate();
 
const handleSendCode = (email: string) => {
  authenticate(
    {
      type: "email",
      emailMode: "otp",
      email,
    },
    {
      onSuccess: () => {
        // This callback will only fire after both email OTP and MFA (if required) are completed
      },
      onError: (error) => {
        // Handle error
        console.error(error);
      },
    },
  );
};

After the user receives the email OTP, they must submit the code to continue.

The signer status will change to AWAITING_EMAIL_AUTH when an OTP code needs to be submitted:

import { useSignerStatus, useAuthenticate } from "@account-kit/react";
import { AlchemySignerStatus } from "@account-kit/signer";
import React, { useEffect } from "react";
 
function EmailOtpVerification() {
  const { status } = useSignerStatus();
  const { authenticate, isPending } = useAuthenticate({
    onError: (error) => {
      // Handle OTP verification errors
      console.error("OTP verification failed:", error);
    },
  });
 
  // Called when user enters their OTP code from email
  const handleVerify = (emailOtp: string) => {
    authenticate({
      type: "otp",
      otpCode: emailOtp,
    });
  };
 
  // Example of prompting user when OTP verification is needed
  useEffect(() => {
    if (status === AlchemySignerStatus.AWAITING_EMAIL_AUTH) {
      // Show OTP input UI to the user
    }
  }, [status]);
 
  return (
    // Your OTP input UI
    <div>{/* OTP input component */}</div>
  );
}

If MFA is required, the signer status will change to AWAITING_MFA_AUTH. You'll need to collect and submit the TOTP code from the user's authenticator app:

import {
  useSignerStatus,
  useSigner,
  useAuthenticate,
} from "@account-kit/react";
import { AlchemySignerStatus } from "@account-kit/signer";
import React, { useEffect, useState } from "react";
 
function MfaVerification() {
  const signer = useSigner();
  const { status } = useSignerStatus();
  const [isVerifying, setIsVerifying] = useState(false);
 
  // Called when user enters their TOTP code from authenticator app
  const handleVerify = async (totpCode: string) => {
    try {
      setIsVerifying(true);
      await signer?.validateMultiFactors({
        multiFactorCode: totpCode,
      });
      // After successful MFA validation, the user will be authenticated
      // and the onSuccess callback from the initial authenticate call will fire
    } catch (error) {
      console.error("MFA verification failed:", error);
    } finally {
      setIsVerifying(false);
    }
  };
 
  // Example of prompting user when MFA verification is needed
  useEffect(() => {
    if (status === AlchemySignerStatus.AWAITING_MFA_AUTH) {
      // Show TOTP input UI to the user
    }
  }, [status]);
 
  return (
    // Your TOTP input UI
    <div>{/* TOTP input component */}</div>
  );
}

Was this page helpful?