Two-factor authentication

In preview

Add a second factor in two calls.

Set up in:
Cursor

Two-factor authentication adds a second proof on top of a password: a one-time code on a channel the user controls. With Bird Verify it's a send call at the moment of login and a check call when the user types the code back — SMS, WhatsApp, or email today, per-country channel config, and nothing to store between the two.

verify.ts
200 · pending
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

// Send the code, then check it by recipient.
await bird.verify.verifications.create({
  to: { phone_number: "+15551234567" },
}).safe();

const { data } = await bird.verify.verifications.check({
  to:   { phone_number: "+15551234567" },
  code: userInput,
}).safe();

2FA is the verification flow, pointed at login.

Under the hood, 2FA is the same flow the Bird Verify API runs everywhere: create a verification for the user's phone or email when they sign in, then check the code they enter. Because the check is by recipient, your login handler keeps no per-attempt state, and the channel a given user gets is decided by your per-country configuration, not hard-coded in the flow. Drop the password in front of it and the same flow becomes passwordless login.

What you wire up for 2FA.

Two calls and a configuration.

  1. 01

    Send on login, check on submit.

    Create a verification when the user authenticates with their first factor; check the code when they enter it. That's the whole integration.

  2. 02

    SMS, WhatsApp, or email as the factor.

    Use the channel you already have for the user: a phone number over SMS or WhatsApp, an email over email. Voice is rolling out for more options.

  3. 03

    Per-app configuration.

    Keep a separate configuration for login versus signup versus high-value actions, each with its own code rules and channel plan.

  4. 04

    Brute force is bounded.

    Attempt lockout and per-recipient send caps come built in, so a second factor doesn't become a new attack surface.

The login-time flow.

Send the code once the password checks out; verify it when the user submits. A wrong code is a result you branch on, not an exception to catch.

two-factor.ts
200
// Send the second factor once the password checks out.
await bird.verify.verifications.create({
  to: { phone_number: user.phone },
}).safe();

const { data } = await bird.verify.verifications.check({
  to:   { phone_number: user.phone },
  code: submitted,
}).safe();

if (data.result) grantSession(user);

2FA FAQ

What's the difference between 2FA and MFA?
Two-factor authentication uses exactly two factors — typically a password plus a one-time code. Multi-factor authentication is the general term for two or more. Bird Verify provides the code-based factor for either.
Which channel should I use for the second factor?
Use the address you already have: a phone number verifies over SMS or WhatsApp, an email address over email. Per-country configuration controls the order and senders per market.
Does adding 2FA mean storing verification state?
No. Bird checks by recipient, so your login handler sends the code and later checks it by the same address — there's no verification id or pending-code record to keep on your side.
Who do my users see the code from?
Authifly, Bird's verification brand. It's the identity on every code your users receive: email arrives from otp@verify.authifly.com or your own verified domain, and SMS and WhatsApp are Authifly-branded. authifly.com is a public page that reassures recipients Authifly sends legitimate one-time codes on a business's behalf. Bird is the platform you build on; Authifly is what the recipient sees.

Two-factor authentication, on the same API as your other channels.

Bird Verify is the code-based factor for your login and signup — SMS, email, and WhatsApp now, voice rolling out.

Start with one channel.
Add the others when you're ready.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Using Claude Code, Cursor, or Codex? Copy a setup prompt and your agent installs the Bird CLI and skills for you. Pick yours:

Cursor