Phone / SMS OTP
Passwordless sign-in with a one-time code over SMS. The same flow as email codes, delivered by Twilio (or any SMS sender). A Radon Pro feature.
Radon Pro
Phone/SMS OTP is a Pro feature and requires a license. See Radon Pro.
Phone OTP is the SMS analogue of email code: the user enters their phone number, gets a numeric code by text, and types it back. Twilio is the default transport, and — like email senders — it's pluggable.
1. Enable the provider
import { twilioSender } from "@radonsdk/auth/pro/sms";
export const auth = new Radon({
adapter: postgresAdapter(pool),
session: { secret: process.env.RADON_SECRET! },
licenseKey: process.env.RADON_LICENSE_KEY,
appName: "Acme",
providers: {
phoneOtp: {
sender: twilioSender({
accountSid: process.env.TWILIO_ACCOUNT_SID!,
authToken: process.env.TWILIO_AUTH_TOKEN!,
from: "+14155550123", // a Twilio number or Messaging Service SID
}),
},
},
});
await auth.init();| Option | Type | Description |
|---|---|---|
sender | SmsSender | Required. Delivers the SMS (e.g. twilioSender(...)). |
ttlMs | number | Code lifetime in ms. Default 300_000 (5 minutes). |
template | (data) => string | Custom SMS body. Receives { code, expiresInMinutes, appName }. |
from | string | Override the sender's default From number. |
2. Send and verify
// Text a code to the phone number.
const { expiresAt } = await auth.phoneOtp.sendCode({ phone: "+14155550123" });
// Verify what the user typed → resolves (or creates) the user.
const { user, created } = await auth.phoneOtp.verify({ phone: "+14155550123", code: "123456" });
const { token } = auth.createSessionToken(user.id);Return shapes
sendCode({ phone, metadata? }) → { expiresAt: Date }.
verify({ phone, code }) → { user: RadonUser, created: boolean }.
Phone numbers are normalized (spaces/dashes/parens stripped, leading + kept).
Phone accounts don't merge by email
A phone sign-in has no verified email, so it resolves by phone identity and does not merge-by-email. If you want to link a phone to an existing email account, do it explicitly after the user has proven both.
Custom SMS body
providers: {
phoneOtp: {
sender: twilioSender({ accountSid, authToken, from }),
template: ({ code, expiresInMinutes, appName }) =>
`${appName}: your code is ${code} (valid ${expiresInMinutes}m).`,
},
}Next steps
2FA / TOTP
Add an authenticator-app second factor (Google Authenticator, 1Password, Authy) on top of any primary sign-in method. A Radon Pro feature.
Framework integrations
Mount Radon's auth routes into your framework in one line. Ten integrations — Next.js, Express, Fastify, Hono, and six more in Pro.