Skip to content

Multi-factor authentication

Alchemy Signer supports Time-based One-Time Passwords (TOTP) multi-factor authentication (MFA). This lets you prompt users to set up a TOTP authenticator (e.g. Google Authenticator) as an additional security factor.

Setting up Multi-Factor Authentication

1. Add a new TOTP factor

Once the user is authenticated, you can call addMfa to enable TOTP. This returns factor details including an ID and setup information that your app can display to the user (e.g. a QR code or otpauth link that the user can scan in Google Authenticator).

example.ts
import { signer } from "./signer";
 
const { multiFactors } = await signer.addMFA({
  multiFactorType: "totp",
});
 
// Display the QR code or secret to the user
const totpUrl = result?.multiFactors[0].multiFactorTotpUrl;
const multiFactorId = result?.multiFactors[0].multiFactorId;

You can show the multiFactorTotpUrl in your UI as a QR code or link for the user to add it to their authenticator app.

2. Verify the TOTP setup

Once the user has scanned the TOTP secret, have them enter the 6-digit code from their authenticator app. Then call verifyMfa:

example.ts
import { signer } from "./signer";
 
await signer.verifyMfa({
  multiFactorId, // from addMfa
  multiFactorCode: "123456",
});

3. Remove a TOTP factor

If a user wants to disable TOTP, call removeMfa with the multiFactorId you want to remove:

example.ts
import { signer } from "./signer";
 
await signer.removeMfa({
  multiFactorIds: [multiFactorId],
});

4. Get a list of existing MFA factors

example.ts
import { signer } from "./signer";
 
const { multiFactors } = await signer.getMfaFactors();

Authenticating Email OTP with multi-factor TOTP

Step 1: Send an OTP to user's email

example.ts
import { signer } from "./signer";
 
signer.authenticate({
  type: "email",
  emailMode: "otp",
  email: "user@mail.com",
});

Step 2: Submit the email OTP code

example.ts
import { signer } from "./signer";
 
signer.authenticate({
  type: "otp",
  otpCode: "EMAIL_OTP_CODE",
});

Step 3: Submit the TOTP code (authenticator app code)

example.ts
import { signer } from "./signer";
 
const user = await signer?.validateMultiFactors({
  multiFactorCode: totpCode,
});

Authenticating Email magic-link with multi-factor TOTP

When calling authenticate with emailMode="magicLink", you can catch a MfaRequiredError. Then you can collect the TOTP code and resubmit.

example.ts
import { MfaRequiredError } from "@account-kit/signer";
import { signer } from "./signer";
 
const promptUserForCode = async () => {
  // Prompt user for TOTP code
  // const totpCode = await promptUserForCode();
 
  return "123456";
};
 
try {
  await signer.authenticate({
    type: "email",
    email: "user@mail.com",
    emailMode: "magicLink",
  });
} catch (err) {
  if (err instanceof MfaRequiredError) {
    // Prompt user for TOTP code
    const totpCode = await promptUserForCode();
 
    const { multiFactorId } = err.multiFactors[0];
    await signer.authenticate({
      type: "email",
      emailMode: "magicLink",
      email: "user@mail.com",
      multiFactors: [
        {
          multiFactorId,
          multiFactorCode: totpCode,
        },
      ],
    });
  } else {
    // handle other errors
  }
}

Authenticating Social Login with multi-factor TOTP

When a user has MFA enabled using an authenticator app, the authentication process for social login is seamless. Unlike email authentication flows, you don't need to handle the MFA challenge manually in your code.

The TOTP verification happens automatically during the OAuth callback flow:

  1. The user authenticates with the social provider (Google, Facebook, etc.)
  2. After successful provider authentication, they're prompted for their TOTP code on the OAuth callback page
  3. Once verified, authentication completes normally

Simply use the standard social login authentication as shown in the Social Login Authentication guide:

example.ts
import { signer } from "./signer";
 
await signer.authenticate({
  type: "oauth",
  authProviderId: "google", // Choose between the auth providers you selected to support from your auth policy
  mode: "redirect", // Alternatively, you can choose "popup" mode
  redirectUrl: "/", // After logging in, redirect to the index page
});