Skip to content
Alchemy Logo

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

You can implement Email OTP authentication in two ways:

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

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

To add authentication in a modal popup:

import React from "react";
import { useAuthModal } from "@account-kit/react";
 
export default function MyPage() {
  const { openAuthModal } = useAuthModal();
 
  return <button onClick={openAuthModal}>Sign in</button>;
}

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

Or:

To embed authentication directly in your page:

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

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

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

import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
import { sepolia, alchemy } from "@account-kit/infra";
 
const uiConfig: AlchemyAccountsUIConfig = {
  auth: {
    sections: [
      [
        {
          type: "email",
          emailMode: "otp",
 
          // Optional customizations:
          buttonLabel: "Continue with Email",
          placeholder: "Enter your email address",
        },
      ],
    ],
  },
};
 
export const config = createConfig(
  {
    transport: alchemy({ apiKey: "your-api-key" }),
    chain: sepolia,
  },
  uiConfig,
);

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

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

import { useAuthenticate } from "@account-kit/react";
 
// Inside your component
const { authenticate } = useAuthenticate();
 
// When the user submits their email
const handleSendCode = (email: string) => {
  authenticate(
    {
      type: "email",
      emailMode: "otp",
      email,
    },
    {
      onSuccess: () => {
        // onSuccess only fires once the entire flow is done (email OTP + optional MFA).
        // It still runs even if the final step completes in another tab/window.
      },
      onError: (error) => {
        // Handle error
      },
    },
  );
};

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

import React from "react";
import { useSignerStatus } from "@account-kit/react";
import { AlchemySignerStatus } from "@account-kit/signer";
 
const TrackStatus = () => {
  const { status } = useSignerStatus();
 
  return (
    <>
      {status === AlchemySignerStatus.AWAITING_EMAIL_AUTH && (
        <div>Prompt the user to enter the OTP code</div>
      )}
    </>
  );
};

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

import { useAuthenticate } from "@account-kit/react";
 
// Inside your component
const { authenticate } = useAuthenticate();
 
// When the user submits the OTP code
const handleVerifyCode = (otpCode: string) => {
  authenticate(
    {
      type: "otp",
      otpCode,
    },
    {
      onSuccess: () => {
        // onSuccess only fires once the entire flow is done (email OTP + optional MFA).
        // It still runs even if the final step completes in another tab/window.
      },
      onError: (error) => {
        // Handle invalid code error
      },
    },
  );
};

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

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

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.

Was this page helpful?