Pular para o conteúdo
whatsapi.sh
Tema
Docs

Códigos de verificação do Supabase pelo WhatsApp

O Supabase continua criando e conferindo o código. Quem entrega é o whatsapi: pelo WhatsApp, do seu próprio número, em vez de SMS. Funciona no Supabase Cloud e no Supabase que você mesmo hospeda.

1 · Crie uma chave e pareie um número

Se ainda não fez, o guia inicial mostra os dois. Os códigos saem do seu número conectado e são contados na chave de API que salvar o hook.

2 · Crie o hook

Pela página Webhooks do painel, ou com uma chamada. O campo template é opcional. Coloque {code} onde o código entra.

import { Whatsapi } from "@whatsapi.sh/sdk";

const wa = new Whatsapi({ apiKey: "wa_your_key" });

const data = await wa.setSupabaseHook({ template: "Your Acme code is {code}" });
200 OK
{
  "data": {
    "secret": "v1,whsec_…",   // shown once: copy it now
    "record": {
      "url": "https://api.whatsapi.sh/webhooks/supabase/send-sms/7d3e…",
      "statusUrl": "https://api.whatsapi.sh/webhooks/supabase/send-sms/7d3e…/status",
      "numberId": null,
      "template": "Your Acme code is {code}",
      "apiKey": { "id": "…", "name": "…", "prefix": "…", "revoked": false },
      "createdAt": "…",
      "updatedAt": "…"
    }
  }
}

O secret só vem na resposta da primeira vez que você salva e quando rotaciona. Copie agora. Passe numberId pra escolher de qual número os códigos saem. Se não passar, eles saem do seu número conectado, desde que você tenha só um. Se não houver número conectado, ou houver mais de um, todos os códigos falham até você escolher. Remover o número escolhido é o mesmo que não escolher nenhum.

3 · Aponte o Supabase pro hook

No Supabase Cloud

  1. No seu projeto, abra Authentication → Auth Hooks e adicione um Send SMS hook. Escolha HTTPS como tipo.
  2. Cole a url da resposta no campo da URL e o secret no campo do segredo, do jeito que vieram. O segredo já começa com v1,whsec_. Clique em Create hook.
  3. Com o hook salvo, abra Authentication → Sign In / Providers → Phone. Ligue o Enable Phone provider e deixe o Enable phone confirmations ligado, pra todo cadastro ser confirmado com código. Coloque SMS OTP Expiry em 300 e salve. Não precisa de provedor de SMS: o hook faz esse papel.

No Supabase que você mesmo hospeda

Adicione estas quatro linhas ao environment do serviço auth (GoTrue) no seu docker-compose.yml. Cole a url e o secret da resposta do jeito que vieram. O segredo já começa com v1,whsec_.

docker-compose.yml
services:
  auth:
    environment:
      GOTRUE_HOOK_SEND_SMS_ENABLED: "true"
      GOTRUE_HOOK_SEND_SMS_URI: "https://api.whatsapi.sh/webhooks/supabase/send-sms/<hook id>"
      GOTRUE_HOOK_SEND_SMS_SECRETS: "<secret from the answer>"
      GOTRUE_SMS_OTP_EXP: "300"

O docker-compose.yml do Supabase já traz essas variáveis comentadas, como # GOTRUE_HOOK_SEND_SMS_URI. Se o seu também tiver, substitua essas linhas em vez de criar outras ao lado. O Compose recusa um arquivo que repete uma variável.

Ligue o login por telefone no .env, que fica ao lado do docker-compose.yml. O compose do Supabase já repassa o ENABLE_PHONE_SIGNUP pro GoTrue como GOTRUE_EXTERNAL_PHONE_ENABLED, e o ENABLE_PHONE_AUTOCONFIRM como GOTRUE_SMS_AUTOCONFIRM. Não repita essas duas no compose.

.env
ENABLE_PHONE_SIGNUP=true
ENABLE_PHONE_AUTOCONFIRM=false

Sem ENABLE_PHONE_SIGNUP=true, cadastro e login por telefone ficam desligados. O arquivo de exemplo do Supabase vem com ENABLE_PHONE_AUTOCONFIRM=true, que confirma o cadastro sem código. Mude pra false. Com o hook ligado, o GoTrue ignora o GOTRUE_SMS_PROVIDER e o template de SMS dele. A mensagem é o template que você salvou aqui.

Depois recrie o serviço auth. O docker compose restart mantém os valores antigos, então rode isto no lugar:

docker compose up -d auth

4 · Teste

supabase-js
const phone = "+5511988887777";

// GoTrue creates a code, and whatsapi sends it to that WhatsApp.
await supabase.auth.signInWithOtp({ phone });

// Then check the code the person typed.
const { data, error } = await supabase.auth.verifyOtp({ phone, token: "482913", type: "sms" });

A mensagem sai do seu número, com o seu template. Deixe type: "sms" como está: pro Supabase, continua sendo um código por telefone.

5 · Só ofereça login por telefone quando ele funciona

O Supabase não desliga o login por telefone sozinho. Se o seu número cair, todo código falha e a pessoa fica presa no formulário. Então, antes de mostrar a opção de telefone, consulte o statusUrl do hook. Ele não pede chave, então dá pra chamar direto do navegador.

JavaScript
// Your hook's statusUrl. It needs no key, so it's safe in the browser.
const statusUrl = "https://api.whatsapi.sh/webhooks/supabase/send-sms/7d3e…/status";

async function phoneSignInWorks() {
  try {
    const res = await fetch(statusUrl);
    return res.ok && (await res.json()).data.ready === true;
  } catch {
    return false; // Can't tell, so offer email instead.
  }
}
200 OK
{ "data": { "ready": true } }

O ready vem true quando a sua conta e a chave de API do hook estão ativas e o número que envia está conectado. A resposta pode ter até 30 segundos. Se a chamada falhar ou não responder 200, trate como indisponível e ofereça e-mail no lugar. Um hook que não existe responde 404.

Ele não sabe se o número ainda tem conversas novas no dia, porque isso depende de quem pede o código. Essa recusa continua chegando pra pessoa, com o motivo.

Tempo limite e validade

O GoTrue dá 5 segundos pra chamada inteira do hook. Passou disso, ele descarta o código, e esse código não serve mais, mesmo que a mensagem chegue depois. Respondemos em até 4 segundos.

O GOTRUE_SMS_OTP_EXP vem com 60 segundos por padrão. É pouco pro WhatsApp: a pessoa precisa ver a mensagem, abrir e digitar o código. Coloque 300, que são cinco minutos. No Supabase Cloud, é o SMS OTP Expiry, nas configurações do provedor Phone.

Quando o código não sai

Dizemos ao GoTrue o motivo, num texto feito pra quem está no formulário, como “We can't send more codes to new numbers today. Try again tomorrow.” (a mensagem vem em inglês). O GoTrue v2.189.0 ou mais novo mostra essa mensagem. Versões anteriores mostram um erro genérico de servidor, então use a v2.189.0 ou mais nova.

Os limites do próprio GoTrue valem antes de ele nos chamar. O GOTRUE_SMS_MAX_FREQUENCY permite um código por telefone a cada 60 segundos, por padrão. O GOTRUE_RATE_LIMIT_SMS_SENT permite 30 códigos por hora pro seu Supabase inteiro, por padrão. Dá pra aumentar. No Supabase Cloud, esse limite por hora é o Rate limit for sending SMS messages, em Authentication → Rate Limits. O limite de conversas novas do número, logo abaixo, continua valendo.

Enquanto não tiver número conectado, todo código é recusado assim. O Supabase não desliga o login por telefone sozinho, então esconda essa opção até o hook voltar a ficar pronto, como mostra o passo 5.

Cada código é uma mensagem no WhatsApp

Cada código sai da cota da sua conta, como qualquer mensagem. O primeiro código pra alguém com quem o seu número nunca conversou também gasta uma das conversas novas do dia desse número. Um número pode começar de 5 a 20 conversas novas por dia, conforme há quanto tempo a linha está em uso, a não ser que você defina outro limite pra ele. O hook envia de um número só, então, quando não sobram mais conversas novas no dia, os códigos pra gente nova são recusados até o dia seguinte. Quem já conversou com o seu número não entra na conta.

Os códigos nunca esperam na fila de envio e nunca mostram “digitando…”, porque tem alguém no formulário aguardando o código. Como não ser banido explica o limite de conversas novas.

Rotacionar o segredo

Salve o hook com rotateSecret: true. A resposta traz um segredo novo, e o antigo continua valendo por 24 horas.

import { Whatsapi } from "@whatsapi.sh/sdk";

const wa = new Whatsapi({ apiKey: "wa_your_key" });

const data = await wa.setSupabaseHook({ rotateSecret: true });

Dentro dessas 24 horas, coloque o segredo novo no Supabase. No Supabase Cloud, abra o Send SMS hook em Authentication → Auth Hooks e cole o segredo novo no lugar do antigo. Se você hospeda o Supabase, coloque em GOTRUE_HOOK_SEND_SMS_SECRETS e recrie o serviço auth. Durante a troca, dá pra deixar os dois ali, separados por |. O GoTrue assina com cada um.

GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_new…|v1,whsec_old…"

Vale saber

  • O template só aceita {code}. Quem decide quando o código vence é o Supabase. Se quiser o prazo na mensagem, escreva no texto e mantenha igual à validade que você definiu no passo 3.
  • Quando alguém troca o número de telefone, o código vai pro número novo.
  • O GoTrue só chama endereços https, fora o localhost. A URI sempre começa com https://api.whatsapi.sh, mesmo que o Supabase rode no mesmo servidor que o whatsapi.

Quando algo dá errado

À esquerda, o erro que o seu app recebe do Supabase.

Hook requires authorization tokenNão conseguimos conferir a assinatura. O segredo do hook no Supabase não bate com o nosso, ou o relógio do servidor está adiantado ou atrasado mais de 5 minutos. Cole o segredo de novo, ou rotacione, e confira o relógio.
Unexpected status code returned from hook: 404Não existe hook nesse endereço. A URI tem um erro de digitação, ou o hook foi apagado. Um hook novo vem com URL e segredo novos, então atualize os dois.
Failed to reach hook within maximum time of 5.000000 secondsO GoTrue não conseguiu falar com a gente em 5 segundos, e esse código não serve mais. Se você hospeda o Supabase, confira se o container do auth consegue acessar https://api.whatsapi.sh: DNS, firewall, proxy. Depois peça um código novo.
Service currently unavailable due to hook / Invalid payload sent to hookEm versões anteriores à v2.189.0, o GoTrue mostra isso no lugar do nosso motivo quando o código não sai. Atualize pra ver o motivo, ou confira no painel.

Se o código não chegar, abra o painel. Confira se o número continua conectado, se a cota não acabou e se o número ainda tem conversas novas no dia. Se a chave de API que salvou o hook foi revogada, os códigos também param. Salve o hook de novo com uma chave ativa.