Email OTP with Multi-Factor Authentication

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

Overview

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

Implementation

Step 1: Start Email OTP Authentication

First, initiate the email OTP authentication process:

1import React from "react";
2import { useAuthenticate } from "@account-kit/react";
3
4// Inside your component
5const { authenticate } = useAuthenticate();
6
7const handleSendCode = (email: string) => {
8 authenticate(
9 {
10 type: "email",
11 emailMode: "otp",
12 email,
13 },
14 {
15 onSuccess: () => {
16 // This callback will only fire after both email OTP and MFA (if required) are completed
17 },
18 onError: (error) => {
19 // Handle error
20 console.error(error);
21 },
22 }
23 );
24};

Step 2: Submit the OTP Code

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:

1import { useSignerStatus, useAuthenticate } from "@account-kit/react";
2import { AlchemySignerStatus } from "@account-kit/signer";
3import React, { useEffect } from "react";
4
5function EmailOtpVerification() {
6 const { status } = useSignerStatus();
7 const { authenticate, isPending } = useAuthenticate({
8 onError: (error) => {
9 // Handle OTP verification errors
10 console.error("OTP verification failed:", error);
11 },
12 });
13
14 // Called when user enters their OTP code from email
15 const handleVerify = (emailOtp: string) => {
16 authenticate({
17 type: "otp",
18 otpCode: emailOtp,
19 });
20 };
21
22 // Example of prompting user when OTP verification is needed
23 useEffect(() => {
24 if (status === AlchemySignerStatus.AWAITING_EMAIL_AUTH) {
25 // Show OTP input UI to the user
26 }
27 }, [status]);
28
29 return (
30 // Your OTP input UI
31 <div>{/* OTP input component */}</div>
32 );
33}

Step 3: Complete Authentication

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:

1import {
2 useSignerStatus,
3 useSigner,
4 useAuthenticate,
5} from "@account-kit/react";
6import { AlchemySignerStatus } from "@account-kit/signer";
7import React, { useEffect, useState } from "react";
8
9function MfaVerification() {
10 const signer = useSigner();
11 const { status } = useSignerStatus();
12 const [isVerifying, setIsVerifying] = useState(false);
13
14 // Called when user enters their TOTP code from authenticator app
15 const handleVerify = async (totpCode: string) => {
16 try {
17 setIsVerifying(true);
18 await signer?.validateMultiFactors({
19 multiFactorCode: totpCode,
20 });
21 // After successful MFA validation, the user will be authenticated
22 // and the onSuccess callback from the initial authenticate call will fire
23 } catch (error) {
24 console.error("MFA verification failed:", error);
25 } finally {
26 setIsVerifying(false);
27 }
28 };
29
30 // Example of prompting user when MFA verification is needed
31 useEffect(() => {
32 if (status === AlchemySignerStatus.AWAITING_MFA_AUTH) {
33 // Show TOTP input UI to the user
34 }
35 }, [status]);
36
37 return (
38 // Your TOTP input UI
39 <div>{/* TOTP input component */}</div>
40 );
41}

Next Steps