DEV Community

Raya
Raya

Posted on Originally published at myotp.app

Supabase phone auth without a built-in provider: the Send SMS Hook, step by step

Supabase ships phone auth with a short list of built-in SMS providers. If the one you want isn't on it, you're not stuck. There's a documented escape hatch called the Send SMS Hook, and it takes about three minutes to wire up. I work at MyOTP.App, so the walkthrough uses us, but the mechanism is the same for anyone who hosts a compatible endpoint.

What the hook is

Supabase doesn't send SMS. Phone login is auth logic, and the message has to leave through somebody's gateway. The provider dropdown is the built-in list. The Send SMS Hook replaces it: when a user requests a phone OTP, Supabase POSTs the code and the phone number to a URL you configure, and your endpoint delivers it however it likes.

Supabase keeps the code and does the verification. The hook is delivery only. Nothing changes in your client code.

Setup, in this order

The order matters. The endpoint URL is created from the secret, so the secret has to exist first.

  1. In Supabase, open Authentication, then Hooks, then Send SMS Hook. Choose the HTTPS type and click Generate secret. Copy the value. It starts with v1,whsec_. Don't save the hook yet.
  2. In your MyOTP dashboard, open Integrations, pick which application should pay for the messages, paste the secret, and create the integration. You get back a URL of the form https://api.myotp.app/v1/supabase/send-sms/<id>.
  3. Back in Supabase, paste that URL into the hook, enable it, and save.

That's it. No SDK, no server code, nothing to deploy.

What Supabase sends

{
  "user": { "id": "8484b834-...", "phone": "919000000001" },
  "sms": { "otp": "123456" }
}
Enter fullscreen mode Exit fullscreen mode

Requests are signed with the Standard Webhooks scheme: webhook-id, webhook-timestamp and webhook-signature headers, verified against the secret with the v1,whsec_ prefix stripped and the rest base64-decoded. If you write your own endpoint, don't skip this. An unauthenticated send-me-an-SMS URL on the public internet has a short and expensive life.

Two things that trip people up

Supabase wants an empty body with a 200. Return a friendly JSON receipt and Supabase may treat the call as failed, so the user sees an error while the SMS is already on its way.

Test with a phone in your hand. Hook failures, provider failures and route failures all look identical from the Supabase side.

Channels

Supabase hands over a code and a phone number and has no opinion about how the code reaches the person. The same hook can deliver over WhatsApp or Telegram instead of SMS. On our side that's a per-application setting. The Supabase side doesn't change.

When not to bother

If your current provider works and your users are all in one country where it delivers well, leave it alone. The hook earns its keep when delivery is patchy in the countries you care about, when per-verification fees stack on top of message cost, or when you want WhatsApp and Telegram without rebuilding auth.

Disclosure again: I work at MyOTP.App. The docs page for this integration is myotp.app/integrations/supabase and it links to Supabase's own hook documentation.

Top comments (0)