Phone number plugin installation and setup
The phone number plugin extends authentication by allowing users to sign in and sign up using their phone number with OTP verification. To install, import phoneNumber from 'better-auth/plugins' and add it to the betterAuth plugins array. The sendOTP configuration option must be provided as a function that implements sending OTP via SMS. The plugin requires database migration or schema generation using 'npx auth migrate' or 'npx auth generate'. On the client, import phoneNumberClient from 'better-auth/client/plugins' and add it to createAuthClient plugins.
Phone number plugin schema additions
The phone number plugin adds two fields to the user table: phoneNumber (string type, unique, optional) and phoneNumberVerified (boolean type, default false, optional). These fields track the user's phone number and verification status.
Send OTP endpoint
The POST endpoint /phone-number/send-otp accepts a phoneNumber parameter (string, required) to send an OTP to the user's phone number for verification.
Verify phone number endpoint
The POST endpoint /phone-number/verify accepts: phoneNumber (string, required), code (string, required, the OTP code), disableSession (boolean, optional, default false to disable session creation), and updatePhoneNumber (boolean, optional, default false to update phone number for existing logged-in user).
Consume OTP on server
The server-only API auth.api.consumePhoneNumberOTP({body: {phoneNumber, code}}) allows consuming an OTP without creating or updating a user or session. This is for custom sign-up or account-linking flows and does not register an HTTP route or generate a client method. It uses the same validation as verifyPhoneNumber but does not return a session or reusable proof of verification.
Sign up with phone number configuration
To enable sign-up on phone number verification, pass signUpOnVerification option with getTempEmail function (required, generates temporary email from phone number) and optionally getTempName function (generates temporary name, defaults to phone number). Custom user schema fields can be passed in the verify request body.
Sign in with phone number endpoint
The POST endpoint /sign-in/phone-number accepts: phoneNumber (string, required), password (string, required), and rememberMe (boolean, optional, default true). Requires a corresponding account record with providerId set to 'credential' in the account table.
Update phone number flow
To update phone number, first send OTP to new phone number using authClient.phoneNumber.sendOtp({phoneNumber}), then verify with authClient.phoneNumber.verify({phoneNumber, code, updatePhoneNumber: true}). Non-null phone number updates through updateUser are blocked; changing phone numbers requires OTP verification.
Remove phone number
Logged-in users can remove their phone number by passing null to authClient.updateUser({phoneNumber: null}). This atomically clears the phone number and resets the verified flag, freeing the number for another account to claim.
Disable session creation on phone verification
By default, the plugin creates a session after phone number verification. Pass disableSession: true to the verify method to disable automatic session creation.
Request password reset with phone number
The POST endpoint /phone-number/request-password-reset accepts phoneNumber (string, required) to initiate password reset by sending OTP to the user's phone number.
Reset password with phone number OTP
The POST endpoint /phone-number/reset-password accepts: otp (string, required, the one-time password), phoneNumber (string, required), and newPassword (string, required, the new password).
Phone number plugin configuration options
Configuration options: otpLength (number, default 6, length of OTP code), sendOTP (required function to send OTP via SMS), expiresIn (number, default 300 seconds, OTP expiration time), callbackOnVerification (function called after phone verification), sendPasswordResetOTP (function to send OTP for password reset), phoneNumberValidator (custom function to validate phone number format), verifyOTP (custom function to verify OTP with external providers like Twilio), signUpOnVerification (object with getTempEmail and getTempName functions), requireVerification (boolean, when enabled users cannot sign in until phone is verified), allowedAttempts (number, default 3, max verification attempts before OTP is deleted).
Phone number verification brute force protection
The phone number plugin includes built-in brute force protection. When a user exceeds allowedAttempts (default 3), the OTP code is automatically deleted and further attempts return 403 (Forbidden) status with 'Too many attempts' message. The user must request a new OTP code to continue.
sendOTP function should not be awaited
It is highly recommended not to await the sendOTP function as it slows down the request and could cause timing attacks. For serverless platforms, use waitUntil to ensure the OTP is sent without blocking the response.
Custom OTP verification example with Twilio
When using the verifyOTP option to integrate with external SMS providers, provide an async function that takes {phoneNumber, code} and ctx parameters. Example: verifyOTP: async ({phoneNumber, code}, ctx) => { const isValid = await twilioClient.verify.services('YOUR_SERVICE_SID').verificationChecks.create({to: phoneNumber, code}); return isValid.status === 'approved'; }
Sign in with phone number requires credential account
To sign in with phone number and password, the user must have a corresponding record in the account table with providerId set to 'credential'. If migrating from another auth provider or seeding users manually, ensure this record exists.
Require phone verification before sign in
When requireVerification is enabled, users cannot sign in with their phone number until it is verified. Unverified users attempting to sign in receive a 401 error (PHONE_NUMBER_NOT_VERIFIED) and automatically trigger an OTP send to start verification.