Phone Number
Add SMS OTP and phone-number sign-in flows.
The phone-number plugin stores a phone number on the user, sends OTP codes through an SMS sender you provide, verifies them, and supports phone-based password reset.
Installation
Add the plugin to your server
Pass phone_number() to your Kernia config and supply a send_sms callback. It
receives the destination number and the message text to deliver via your SMS
provider (Twilio, Vonage, SNS).
import os
from kernia import KerniaOptions
from kernia.auth import init
from kernia.plugins.phone_number import phone_number
async def send_sms(phone: str, message: str) -> None:
await sms_client.messages.create(to=phone, body=message)
auth = init(KerniaOptions(
database=adapter,
secret=os.environ["KERNIA_SECRET"],
base_url=os.environ["KERNIA_BASE_URL"],
plugins=[phone_number()],
advanced={"phone-number": {"send_sms": send_sms}},
))Add the client plugin
Kernia speaks the Better Auth wire protocol, so the official JavaScript client
works unchanged. Add the phoneNumber client plugin:
import { createAuthClient } from "better-auth/client";
import { phoneNumberClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
baseURL: "/api/auth",
plugins: [phoneNumberClient()],
});Usage
Verify a phone number with an OTP
Send a code, then verify it. On a successful verify the number is marked
phoneNumberVerified and a session is issued.
await authClient.phoneNumber.sendOtp({
phoneNumber: "+15551234567",
});
await authClient.phoneNumber.verify({
phoneNumber: "+15551234567",
code: "123456",
});Sign in with phone number and password
For accounts that already have a password, sign in directly with the phone number.
await authClient.signIn.phoneNumber({
phoneNumber: "+15551234567",
password: "the-password",
rememberMe: true,
});Reset a password over SMS
await authClient.phoneNumber.requestPasswordReset({
phoneNumber: "+15551234567",
});
await authClient.phoneNumber.resetPassword({
phoneNumber: "+15551234567",
otp: "123456",
newPassword: "new-password",
});Options
Configure under advanced["phone-number"]:
| Option | Type | Default | Description |
|---|---|---|---|
send_sms | async (phone, message) -> None | — | Required. Sends the SMS containing the OTP. |
otp_length | int | 6 | Number of digits in the generated code. |
expires_in | int | 300 | Code lifetime in seconds. |
disable_sign_up | bool | False | Reject OTP sign-ins for numbers with no existing account. |
Schema
The plugin extends the core user table:
phoneNumberphoneNumberVerifiedOTP codes themselves live on the core verification table — no extra migration
beyond these two user columns.
Numbers are stored as supplied, so normalize to a canonical format (e.g. E.164)
before passing them to the client to keep the phoneNumber uniqueness
constraint meaningful.