Better Auth phone numbers with a five-line OTP adapter

typescript dev.to

Better Auth's phoneNumber() plugin does the hard part of phone login. It generates the code, stores it, verifies it and manages the session. What it leaves to you is delivery: a sendOTP callback that you fill in. I work at MyOTP.App and we publish an adapter for that callback, so here's the whole setup.

Install

npm install @myotp/better-auth better-auth
Enter fullscreen mode Exit fullscreen mode

Get a key at myotp.app/sign-up. 15 trial credits, no card.

Wire it

import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins";
import { myotpSendOtp } from "@myotp/better-auth";

export const auth = betterAuth({
  database: yourDatabase(),
  plugins: [
    phoneNumber({
      sendOTP: myotpSendOtp({ apiKey: process.env.MYOTP_API_KEY! }),
      otpLength: 6,
      expiresIn: 300,
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

That's it. Better Auth generates and verifies, MyOTP delivers.

Channel

myotpSendOtp({
  apiKey: process.env.MYOTP_API_KEY!,
  channel: "whatsapp",   // or "telegram", default "sms"
  brand: "Acme",         // optional sender name
})
Enter fullscreen mode Exit fullscreen mode

To let the user choose at signup, wrap it: read the channel from your own request context and call myotpSendOtp with that value. The README has the wrapper.

Two things to know

Better Auth owns the code, so MyOTP never sees the verification. That means our delivery report tells you the message arrived, and Better Auth tells you whether the user typed it right. Two systems, two logs, and you'll want both when debugging a "code never came" ticket.

The key lives in your server environment. The adapter runs inside Better Auth on the server, so it never reaches the client bundle. Allowlist the server's public IP on the key in the MyOTP dashboard, or the first send from production comes back 403. That 403 means the allowlist, not a bad key.

Why not just call Twilio in the callback

You can. The callback is a function, it doesn't care. The reasons people pick us for it are setup, price outside the US, and WhatsApp and Telegram as channels without going through Meta's business verification. If your users are US-only and you already have a Twilio account, stay put.

Disclosure again: I work at MyOTP.App. The adapter is MIT, on npm and GitHub at brntech/myotp-agentkit.

Source: dev.to

arrow_back Back to Tutorials