Skip to main content
The phone number plugin lets users sign in and sign up using their phone number. It sends a one-time password (OTP) via SMS for verification.

Installation

1

Add the server plugin

Import phoneNumber and provide a sendOTP function that delivers the code via SMS:
auth.ts
import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    phoneNumber({
      sendOTP: ({ phoneNumber, code }, ctx) => {
        // Send the OTP code to the phone number via your SMS provider
        await smsProvider.send({ to: phoneNumber, body: `Your code: ${code}` });
      },
    }),
  ],
});
Do not await the sendOTP call. Awaiting it slows down the request and can cause timing attacks. On serverless platforms, use waitUntil to ensure the SMS is sent before the function exits.
2

Run the database migration

The plugin adds phoneNumber and phoneNumberVerified columns to the user table:
npx auth migrate
3

Add the client plugin

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { phoneNumberClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [
    phoneNumberClient(),
  ],
});

Usage

Send an OTP

Call phoneNumber.sendOtp on the client to trigger the sendOTP callback on the server:
send-otp.ts
import { authClient } from "@/lib/auth-client";

await authClient.phoneNumber.sendOtp({
  phoneNumber: "+1234567890",
});

Verify the OTP

After the user enters the code, call phoneNumber.verify:
verify-otp.ts
import { authClient } from "@/lib/auth-client";

const { data, error } = await authClient.phoneNumber.verify({
  phoneNumber: "+1234567890",
  code: "123456",
  disableSession: false,    // set true to verify without creating a session
});
By default, verifying a phone number creates a session and signs the user in.

Sign up on first verification

To automatically create a new user when a previously unseen phone number is verified, configure signUpOnVerification:
auth.ts
phoneNumber({
  sendOTP: ({ phoneNumber, code }, ctx) => { /* ... */ },
  signUpOnVerification: {
    getTempEmail: (phoneNumber) => `${phoneNumber}@my-site.com`,
    getTempName: (phoneNumber) => phoneNumber, // optional
  },
})

Sign in with phone number and password

If users have a password, they can sign in directly without an OTP:
sign-in.ts
import { authClient } from "@/lib/auth-client";

const { data, error } = await authClient.signIn.phoneNumber({
  phoneNumber: "+1234567890",
  password: "password1234",
  rememberMe: true,
});

Update phone number

To change a user’s phone number, send an OTP to the new number and then verify it with updatePhoneNumber: true:
update-phone.ts
import { authClient } from "@/lib/auth-client";

// Step 1: send OTP to new number
await authClient.phoneNumber.sendOtp({
  phoneNumber: "+19876543210",
});

// Step 2: verify with updatePhoneNumber flag
await authClient.phoneNumber.verify({
  phoneNumber: "+19876543210",
  code: "123456",
  updatePhoneNumber: true,
});

Password reset via phone number

1

Request a reset OTP

request-reset.ts
await authClient.phoneNumber.requestPasswordReset({
  phoneNumber: "+1234567890",
});
2

Reset the password

reset-password.ts
await authClient.phoneNumber.resetPassword({
  phoneNumber: "+1234567890",
  otp: "123456",
  newPassword: "newSecurePassword",
});

Configuration options

OptionTypeDefaultDescription
sendOTPfunctionRequired. Sends the OTP code. Receives { phoneNumber, code } and a context object.
otpLengthnumber6Number of digits in the OTP.
expiresInnumber300OTP lifetime in seconds.
allowedAttemptsnumber3Max verification attempts before the OTP is deleted.
requireVerificationbooleanfalseBlock sign-in until phone number is verified.
signUpOnVerificationobjectAuto-create users on first verification. Requires getTempEmail.
callbackOnVerificationfunctionCalled after a successful verification. Receives { phoneNumber, user }.
phoneNumberValidatorfunctionCustom phone number validation function. Returns a boolean.
verifyOTPfunctionOverride the default OTP verification logic (e.g. to use Twilio Verify).
sendPasswordResetOTPfunctionCustom OTP sender for password reset flow.

Database schema

The plugin adds two optional columns to the user table:
ColumnTypeDescription
phoneNumberstring (unique)The user’s phone number.
phoneNumberVerifiedbooleanWhether the phone number has been verified.