Radon
Auth Methods

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

lib/auth.ts
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();
OptionTypeDescription
senderSmsSenderRequired. Delivers the SMS (e.g. twilioSender(...)).
ttlMsnumberCode lifetime in ms. Default 300_000 (5 minutes).
template(data) => stringCustom SMS body. Receives { code, expiresInMinutes, appName }.
fromstringOverride 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

On this page