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:
- Verify the user's email with a one-time password
- Verify the 6-digit code (TOTP) from their authenticator app
Implementation
Step 1: Start Email OTP Authentication
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);
},
}
);
};
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:
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>
);
}
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:
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>
);
}