Email OTP Authentication

Email OTP (One-Time Password) authentication is a two-step process:

  1. The user enters their email address and requests a verification code
  2. The user enters the 6-digit code they receive in their inbox to complete authentication

Overview

You can implement Email OTP authentication in two ways:

Pre-built UI Components

Smart Wallets provides pre-built UI components that handle the entire Email OTP authentication flow with minimal code.

Step 1: Add Authentication Components to Your Page

Before configuring your authentication, first add one of the pre-built components to your application:

Using Modal Authentication

To add authentication in a modal popup:

1import React from "react";
2import { useAuthModal } from "@account-kit/react";
3
4export default function MyPage() {
5 const { openAuthModal } = useAuthModal();
6
7 return <button onClick={openAuthModal}>Sign in</button>;
8}

For more details on modal configuration, see the Modal Authentication documentation.

Or:

Using Embedded Authentication

To embed authentication directly in your page:

1import React from "react";
2import { AuthCard } from "@account-kit/react";
3
4export default function MyLoginPage() {
5 return (
6 <div className="flex flex-row p-4 bg-white border border-gray-200 rounded-lg">
7 <AuthCard />
8 </div>
9 );
10}

For more details on embedded authentication, see the Embedded Authentication documentation.

Step 2: Configure Email OTP in UI Components

After adding the components, configure the Email OTP authentication in your application config:

1import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
2import { sepolia, alchemy } from "@account-kit/infra";
3
4const uiConfig: AlchemyAccountsUIConfig = {
5 auth: {
6 sections: [
7 [
8 {
9 type: "email",
10 emailMode: "otp",
11
12 // Optional customizations:
13 buttonLabel: "Continue with Email",
14 placeholder: "Enter your email address",
15 },
16 ],
17 ],
18 },
19};
20
21export const config = createConfig(
22 {
23 transport: alchemy({ apiKey: "your-api-key" }),
24 chain: sepolia,
25 },
26 uiConfig,
27);

Custom UI

If you need complete control over the user experience, you can implement your own custom UI for Email OTP authentication using Smart Wallets hooks.

Step 1: Send the OTP

First, prompt your user for their email address and send an OTP:

1import { useAuthenticate } from "@account-kit/react";
2
3// Inside your component
4const { authenticate } = useAuthenticate();
5
6// When the user submits their email
7const handleSendCode = (email: string) => {
8 authenticate(
9 {
10 type: "email",
11 emailMode: "otp",
12 email,
13 },
14 {
15 onSuccess: () => {
16 // onSuccess only fires once the entire flow is done (email OTP + optional MFA).
17 // It still runs even if the final step completes in another tab/window.
18 },
19 onError: (error) => {
20 // Handle error
21 },
22 },
23 );
24};

Step 2: Show OTP Input on Status Change

Use the useSignerStatus hook and AlchemySignerStatus enum to react to status changes:

1import React from "react";
2import { useSignerStatus } from "@account-kit/react";
3import { AlchemySignerStatus } from "@account-kit/signer";
4
5const TrackStatus = () => {
6 const { status } = useSignerStatus();
7
8 return (
9 <>
10 {status === AlchemySignerStatus.AWAITING_EMAIL_AUTH && (
11 <div>Prompt the user to enter the OTP code</div>
12 )}
13 </>
14 );
15};

Step 3: Verify the OTP

Once the user receives the code, they’ll enter it in your application:

1import { useAuthenticate } from "@account-kit/react";
2
3// Inside your component
4const { authenticate } = useAuthenticate();
5
6// When the user submits the OTP code
7const handleVerifyCode = (otpCode: string) => {
8 authenticate(
9 {
10 type: "otp",
11 otpCode,
12 },
13 {
14 onSuccess: () => {
15 // onSuccess only fires once the entire flow is done (email OTP + optional MFA).
16 // It still runs even if the final step completes in another tab/window.
17 },
18 onError: (error) => {
19 // Handle invalid code error
20 },
21 },
22 );
23};

Step 4: Check authentication status

Use the useSignerStatus hook to determine if the user is authenticated:

1import { useSignerStatus } from "@account-kit/react";
2
3// Inside your component
4const { isConnected } = useSignerStatus();
5
6// You can use isConnected to conditionally render UI

Next Steps

Add Authenticator App (TOTP) Verification (Optional)

If you’d like to add a second security step to Email OTP, you can enable Multi-Factor Authentication. This prompts users for a 6-digit TOTP code from their authenticator app (e.g. Google Authenticator, Authy) after they verify their email.