docs/content/docs/plugins/phone-number.mdx
The phone number plugin extends the authentication system by allowing users to sign in and sign up using their phone number. It includes OTP (One-Time Password) functionality to verify phone numbers.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { phoneNumber } from "better-auth/plugins" // [!code highlight]
const auth = betterAuth({
plugins: [
phoneNumber({ // [!code highlight]
sendOTP: ({ phoneNumber, code }, ctx) => { // [!code highlight]
// Implement sending OTP code via SMS // [!code highlight]
} // [!code highlight]
}) // [!code highlight]
]
})
```
Run the migration or generate the schema to add the necessary fields and tables to the database.
<Tabs items={["migrate", "generate"]}>
<Tab value="migrate">
```package-install
npx auth migrate
```
</Tab>
<Tab value="generate">
```package-install
npx auth generate
```
</Tab>
</Tabs>
See the [Schema](#schema) section to add the fields manually.
```ts title="auth-client.ts"
import { createAuthClient } from "better-auth/client"
import { phoneNumberClient } from "better-auth/client/plugins" // [!code highlight]
const authClient = createAuthClient({
plugins: [
phoneNumberClient() // [!code highlight]
]
})
```
To send an OTP to a user's phone number for verification, you can use the sendVerificationCode endpoint.
After the OTP is sent, users can verify their phone number by providing the code.
<APIMethod path="/phone-number/verify" method="POST"> ```ts type verifyPhoneNumber = { /** * Phone number to verify. */ phoneNumber: string = "+1234567890" /** * OTP code. */ code: string = "123456" /** * Disable session creation after verification. */ disableSession?: boolean = false /** * Update the phone number of an existing logged-in user. * Requires an active session. */ updatePhoneNumber?: boolean = false } ``` </APIMethod>To allow users to sign up using their phone number, you can pass signUpOnVerification option to your plugin configuration. It requires you to pass getTempEmail function to generate a temporary email for the user.
import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
phoneNumber({
sendOTP: ({ phoneNumber, code }, ctx) => {
// Implement sending OTP code via SMS
},
signUpOnVerification: {
getTempEmail: (phoneNumber) => {
return `${phoneNumber}@my-site.com`
},
//optionally, you can also pass `getTempName` function to generate a temporary name for the user
getTempName: (phoneNumber) => {
return phoneNumber //by default, it will use the phone number as the name
}
}
})
]
})
If you have additional required fields in your user schema, you can pass them in the verify request body:
await authClient.phoneNumber.verify({
phoneNumber: "+1234567890",
code: "123456",
customField: "custom-value", // additional field [!code highlight]
})
In addition to signing in a user using send-verify flow, you can also use phone number as an identifier and sign in a user using phone number and password.
<Callout type="warn"> To sign in with a phone number and password, the user must have a corresponding record in the `account` table with the `providerId` set specifically to `"credential"`. If you are migrating from another auth provider or seeding users manually, ensure this record exists. </Callout> <APIMethod path="/sign-in/phone-number" method="POST"> ```ts type signInPhoneNumber = { /** * Phone number to sign in. */ phoneNumber: string = "+1234567890" /** * Password to use for sign in. */ password: string /** * Remember the session. */ rememberMe?: boolean = true } ``` </APIMethod>Already logged-in users can change their phone number to a new one. First, send an OTP to the new phone number:
import { authClient } from "@/lib/auth-client";
await authClient.phoneNumber.sendOtp({
phoneNumber: "+1234567890" // New phone number // [!code highlight]
})
Then verify the new phone number with updatePhoneNumber: true:
import { authClient } from "@/lib/auth-client";
const isVerified = await authClient.phoneNumber.verify({
phoneNumber: "+1234567890",
code: "123456",
updatePhoneNumber: true // [!code highlight]
})
Logged-in users can release their phone number by passing null to updateUser. The plugin atomically clears the phone number and resets the verified flag, freeing the number so another account can claim it through the standard verification flow.
import { authClient } from "@/lib/auth-client";
await authClient.updateUser({
phoneNumber: null // [!code highlight]
})
For security, non-null phone number updates through updateUser remain blocked. Changing to a different phone number always requires OTP verification via verify with updatePhoneNumber: true.
By default, the plugin creates a session for the user after verifying the phone number. You can disable this behavior by passing disableSession: true to the verify method.
import { authClient } from "@/lib/auth-client";
const isVerified = await authClient.phoneNumber.verify({
phoneNumber: "+1234567890",
code: "123456",
disableSession: true // [!code highlight]
})
To initiate a request password reset flow using phoneNumber, you can start by calling requestPasswordReset on the client to send an OTP code to the user's phone number.
Then, you can reset the password by calling resetPassword on the client with the OTP code and the new password.
otpLengthThe length of the OTP code to be generated. Default is 6.
sendOTPA function that sends the OTP code to the user's phone number. It takes the phone number and the OTP code as arguments.
expiresInThe time in seconds after which the OTP code expires. Default is 300 seconds.
callbackOnVerificationA function that is called after the phone number is verified. It takes the phone number and the user object as the first argument and a request object as the second argument.
import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
phoneNumber({
sendOTP: ({ phoneNumber, code }, ctx) => {
// Implement sending OTP code via SMS
},
callbackOnVerification: async ({ phoneNumber, user }, ctx) => { // [!code highlight]
// Implement callback after phone number verification // [!code highlight]
} // [!code highlight]
})
]
})
sendPasswordResetOTPA function that sends the OTP code to the user's phone number for password reset. It takes the phone number and the OTP code as arguments.
phoneNumberValidatorA custom function to validate the phone number. It takes the phone number as an argument and returns a boolean indicating whether the phone number is valid.
verifyOTPA custom function to verify the OTP code. When provided, this function will be used instead of the default internal verification logic. This is useful when you want to integrate with external SMS providers that handle OTP verification (e.g., Twilio Verify, AWS SNS). The function takes an object with phoneNumber and code properties and a request object, and returns a boolean or a promise that resolves to a boolean indicating whether the OTP is valid.
import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
phoneNumber({
sendOTP: ({ phoneNumber, code }, ctx) => {
// Send OTP via your SMS provider
},
verifyOTP: async ({ phoneNumber, code }, ctx) => { // [!code highlight]
// Verify OTP with your desired logic (e.g., Twilio Verify) // [!code highlight]
// This is just an example, not a real implementation. // [!code highlight]
const isValid = await twilioClient.verify // [!code highlight]
.services('YOUR_SERVICE_SID') // [!code highlight]
.verificationChecks // [!code highlight]
.create({ to: phoneNumber, code }); // [!code highlight]
return isValid.status === 'approved'; // [!code highlight]
} // [!code highlight]
})
]
})
signUpOnVerificationAn object with the following properties:
getTempEmail: A function that generates a temporary email for the user. It takes the phone number as an argument and returns the temporary email.getTempName: A function that generates a temporary name for the user. It takes the phone number as an argument and returns the temporary name.requireVerificationWhen enabled, users cannot sign in with their phone number until it has been verified. If an unverified user attempts to sign in, the server will respond with a 401 error (PHONE_NUMBER_NOT_VERIFIED) and automatically trigger an OTP send to start the verification process.
The plugin requires 2 fields to be added to the user table
export const phoneNumberUserTableFields = [ { name: "phoneNumber", type: "string", description: "The phone number of the user", isUnique: true, isOptional: true, }, { name: "phoneNumberVerified", type: "boolean", description: "Whether the phone number is verified or not", defaultValue: false, isOptional: true, }, ];
<DatabaseTable name="user" fields={phoneNumberUserTableFields} />The phone number plugin includes a built-in protection against brute force attacks by limiting the number of verification attempts for each OTP code.
phoneNumber({
allowedAttempts: 3, // default is 3
// ... other options
})
When a user exceeds the allowed number of verification attempts:
Example error response after exceeding attempts:
{
"error": {
"status": 403,
"message": "Too many attempts"
}
}