BuildBaseBuildBase

Bot Protection

Stop scripted sign-ups and code requests with Cloudflare Turnstile, and refuse disposable email addresses.

A sign-up form that sends email is a free mail cannon for anyone with a script. Each fake sign-up or magic-link request sends a real message from your domain, and enough of them hurt your sender reputation long before they hurt anything else. Bot protection puts a Cloudflare Turnstile check in front of those requests on your hosted sign-in page. Most people never see a puzzle: Turnstile decides from the browser, and only asks for a click when it is unsure.

Turn it on under User Management → Access → Authentication → Security. There is nothing to change in your app; the hosted page shows the check and sends its answer with the form.

Step by step, with screenshots: Set up bot protection.

Modes

ModeWhat is checked
Off (default)Nothing.
AlwaysEvery sign-up, every magic link, email code and phone code sent, and every email and password sign-in.
Only when suspiciousSign-ups, magic links, email codes and phone codes, always. A password sign-in only after three failed sign-ins from the same IP address within 15 minutes.

Why do sign-ups get checked in both modes? Because that is where the cost is. A bot that registers or asks for a code makes us send an email on your behalf, or your SMS provider send a text you pay for, while a bot guessing passwords only gets told no, and the per-IP limits and the per-account lockout already slow that down. Only when suspicious keeps the check out of the way of people who type their password right, and brings it in as soon as one IP address starts failing.

Continue as guest is checked like a sign-up in both modes, since one click makes an account. See Guest users.

The check runs on the server before anything else: before the address is looked up, before a code or link is created, before a password is compared. A request without a valid answer is refused with nothing stored and nothing sent.

Which keys check your page

A Turnstile key pair lists the hostnames its widget may run on, so which pair checks your page depends on where it is served.

  • On the BuildBase sign-in domain, BuildBase's own keys are used. Pick a mode and it works; there is nothing to set up.
  • On your own custom auth domain, BuildBase's keys cannot vouch for a hostname they do not list, so you add a key pair of your own. Until you do, bot protection is off there whatever mode you pick, and the Security screen says so.

Adding your own keys for a custom domain

  1. In the Cloudflare dashboard, open Turnstile and add a widget. Turnstile is free, and you do not need to move your DNS to Cloudflare.
  2. Add your auth domain (for example auth.yourapp.com) as a hostname. The Managed widget mode is the right default.
  3. Copy the site key and the secret key into the bot protection card on the Security screen, and save.

We check the secret with Cloudflare when you save, so a mistyped secret is refused there rather than quietly checking nothing. The secret is stored encrypted and is never shown again: the card only says that keys are saved. To change them, paste a new pair. Remove keys deletes both.

What people see when a check fails

A request whose check failed, expired or was already used gets a short message asking the person to try the check again. Each answer from the widget works once and only for a few minutes, so an answer copied from one request and replayed on another is refused.

If our server cannot reach Cloudflare at all, the request is allowed and we log it. The same goes when our side fails: if the server cannot read your bot protection settings or your secret key, or the check itself throws an error, the request is allowed and the log says why. Bot protection is a speed bump in front of the rate limits and lockouts that still apply, and an outage, Cloudflare's or ours, should not stop your sign-ups. Otherwise, a request with no answer, or one Cloudflare turned down, is refused.

Every refused sign-up, magic link, email code, phone code or guest emits auth.signup_challenged, which you can receive as a webhook. Its payload says which form it was (surface: register, magic-link, email-code, phone-code or guest) and why (reason: missing or refused). A sudden run of them is how you notice a script working through your sign-up page.

If you call the auth API yourself

The hosted page does this for you. A client of your own sends the widget's answer as turnstileToken in the body of POST /api/v1/auth/register, /login, /magic-link, /otp/send, /phone/send, /phone/register, /wallet/register and /guest, and reads the site key from botProtection.siteKey in GET /api/v1/auth/config. A refusal is a 403 with a reason:

reasonMeaning
bot_check_required/login only: this sign-in needs a check it did not send, because the IP address has failed too many sign-ins. Show the widget and retry.
bot_check_failedThe answer was missing, expired, already used or refused.

Disposable email addresses

Throwaway inboxes are how one person turns into a stack of trial accounts. Turn on Refuse disposable email addresses at sign-up on the Security screen, and an address at a known disposable-mail provider is refused with "Please use a permanent email address."

  • It applies to new accounts, whether from the register form or an email-code sign-up, and to a user changing their account's email address.
  • It never applies to signing in. An account that already exists keeps working, so turning this on cannot lock anyone out.
  • The list comes from the open-source disposable-email-domains project and ships with BuildBase, so the check never waits on another service. A subdomain of a listed domain counts as listed, since these services hand out subdomains freely.

It is off by default, and it sits apart from bot protection: one stops scripts, the other stops people who would rather not give you a real address. Many apps want the first and not the second.

Block throwaway email addresses on the Sign-up tab does the same thing with its own list: either switch turns it on, and someone invited to a workspace is never refused. The same tab's Security check on sign-up puts your Forms captcha on the register form. It is separate from Turnstile, and when both are on, the form shows both.

Self-hosted

On a self-hosted install there are no BuildBase keys to fall back on, so you bring your own. Set TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY for your install's sign-in domain (see Configuration), with that domain listed on the widget. An org that saves its own keys on the Security screen uses those instead, and an org on a custom domain needs its own, as in the cloud.

With neither set, bot protection stays off and the Security screen says why. The server also needs to reach challenges.cloudflare.com; if it cannot, every request is allowed, as described above, and the logs say so.