Skip to main content

AuthTwilioChannel

WIP

Twilio Programmable Messaging for SMS magic-link and MFA codes.

Install

bun add twilio

Setup

import { AuthTwilioChannel } from '@gentleduck/auth/channels/twilio'

const channel = new AuthTwilioChannel({
  accountSid: process.env.TWILIO_ACCOUNT_SID!,
  authToken: process.env.TWILIO_AUTH_TOKEN!,
  from: '+15005550006',   // your purchased Twilio phone number
  kind: 'sms',
  id: 'twilio',
})

For toll-free numbers in the US, ensure your sending profile is verified - otherwise SMS will fail.

import { magicLink } from '@gentleduck/auth/providers/magic-link'

auth.providers.use(
  magicLink({
    channels: {
      email: emailChannel,
      sms: channel,
    },
    findIdentityByEmail: (email) => auth.identities.findByEmail(email),
  }),
)

// Client picks SMS:
await client.beginProvider('magic-link', { email: 'ada@example.com', channel: 'sms' })

Important: magic-link over SMS still uses email as the identity key - the user enters their email; the SMS goes to the phone number on the identity. You need to seed the phone in the identity profile (or collect it before sending).

Templating

Default body:

Your sign-in link: https://app.example.com/auth/magic-link/callback?token=...
Expires in 10 minutes.

Customise by subclassing:

class BrandedTwilio extends AuthTwilioChannel {
  async renderMessage({ templateId, vars }) {
    return {
      to: vars.phone,   // resolve from identity.profile.phone
      body: `${vars.appName}: tap to sign in -> ${vars.url}`,
    }
  }
}

Carrier filtering

Carriers in the US (Verizon, AT&T) filter messages aggressively. To maximise deliverability:

MFA codes

For 6-digit MFA codes (TOTP / phone verification):

class MfaTwilio extends AuthTwilioChannel {
  async renderMessage({ templateId, vars }) {
    if (templateId === 'mfa-code') {
      return {
        to: vars.phone,
        body: `Your ${vars.appName} verification code is ${vars.code}.`,
      }
    }
    return super.renderMessage({ templateId, vars })
  }
}

For SMS-based MFA, consider WebAuthn-MFA instead - it's phishing- resistant, where SMS is increasingly attacked via SIM-swap fraud.