feat(auth): require a captcha on the endpoints that send mail (#32)

Signup had no bot protection. The account itself was never the prize — GoTrue
refuses a session until the address is confirmed, and 0 of the 2 unconfirmed
accounts on record ever signed in. The prize is our mail: /signup and /recover
send to whatever address the caller types, so a stranger could make
pezkuwichain.io mail thousands of people who never asked for it. Sending
reputation takes months to rebuild and every product on the domain shares it.

Rate limiting alone was not enough. GoTrue defaults to 30 emails/hour, which is
720 a day to attacker-chosen recipients; tightening it to 15 halves the number
without changing what is possible.

Verification runs inside GoTrue, not here. It POSTs to siteverify with the
widget secret before /signup, /recover, /resend, /magiclink, /otp and /token
with grant_type=password. Adding our own check in the frontend or a proxy would
protect nothing, because /auth/v1/signup is reachable directly.

One shared widget in execute mode rather than one per form: some of these are
buttons with nowhere to put a checkbox, and interaction-only appearance keeps
the challenge invisible unless Cloudflare asks for one. Tokens are single-use,
so each call renders fresh and tears down the previous widget.

Six call sites, matching the six protected endpoints — password login included,
since /token with grant_type=password is enforced. Refresh-token grants are
exempt, so open sessions are unaffected.

Site key travels as a build arg like the WalletConnect id: it is public by
construction, visible in the bundle. Only the secret is privileged and it lives
in GoTrue's env on the auth host.

Ships ahead of enforcement. Turning captcha on server-side before the frontend
sends tokens would break every login and signup in the same instant.
This commit is contained in:
SatoshiQaziMuhammed authored and GitHub committed 2026-08-01 03:45:51 -07:00
1 parent 35884cb8c9
commit c26eeee12c
8 files changed
+153 -2

No files matched your search

+4
View File
@@ -26,6 +26,9 @@ ARG VITE_WS_ENDPOINT_FALLBACK_1=wss://mainnet.pezkuwichain.io
ARG VITE_ASSET_HUB_ENDPOINT=wss://asset-hub-rpc.pezkuwichain.io
ARG VITE_PEOPLE_CHAIN_ENDPOINT=wss://people-rpc.pezkuwichain.io
ARG VITE_WALLETCONNECT_PROJECT_ID=8292a793b7640e8364c378e331e76d04
# Public by design — the site key is visible in the shipped bundle. The
# matching secret lives only in GoTrue's env on the auth host.
ARG VITE_TURNSTILE_SITE_KEY=0x4AAAAAAEDkUkbsUgw2D9zN
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_ANON_KEY
@@ -35,6 +38,7 @@ ENV VITE_WS_ENDPOINT_FALLBACK_1=$VITE_WS_ENDPOINT_FALLBACK_1
ENV VITE_ASSET_HUB_ENDPOINT=$VITE_ASSET_HUB_ENDPOINT
ENV VITE_PEOPLE_CHAIN_ENDPOINT=$VITE_PEOPLE_CHAIN_ENDPOINT
ENV VITE_WALLETCONNECT_PROJECT_ID=$VITE_WALLETCONNECT_PROJECT_ID
ENV VITE_TURNSTILE_SITE_KEY=$VITE_TURNSTILE_SITE_KEY
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
ENV VITE_SUPABASE_ANON_KEY=$VITE_SUPABASE_ANON_KEY