Magic-code and password sign-in are abuse magnets — bot networks send-code-spam every known email to phish, run credential-stuffing dictionaries againstDocumentation Index
Fetch the complete documentation index at: https://docs.pylonsync.com/llms.txt
Use this file to discover all available pages before exploring further.
/password/login, or burn through SMS quotas via /phone/send-code. Pylon’s built-in CAPTCHA gate sits in front of those endpoints; configure one env var pair and unauthenticated bots get bounced before they reach the rate limiter.
Three providers ship in the box:
- hCaptcha — independent, privacy-focused, free up to ~1M/month
- Cloudflare Turnstile — invisible challenge, free for any volume
- Google reCAPTCHA — v2 + v3 token shapes
Gated endpoints
WhenPYLON_CAPTCHA_PROVIDER + PYLON_CAPTCHA_SECRET are both set, these endpoints require a captchaToken in the request body:
| Endpoint | Method | Why gated |
|---|---|---|
/api/auth/magic/send | POST | Bot can send-code-spam every email in a list to phish or harvest |
/api/auth/password/register | POST | Trivial account creation → spam content / abuse fanout |
/api/auth/phone/send-code | POST | Burns Twilio (or other SMS provider) quotas |
/api/auth/magic-link/send | POST | Same as magic/send but for password-reset links |
Configuration
turnstileandcloudflareboth select Turnstile.recaptchaandgoogleboth select reCAPTCHA.
siteverify endpoint with the supplied token + the request’s peer IP:
- hCaptcha:
https://api.hcaptcha.com/siteverify - Turnstile:
https://challenges.cloudflare.com/turnstile/v0/siteverify - reCAPTCHA:
https://www.google.com/recaptcha/api/siteverify
PYLON_CAPTCHA_SECRET. Get both from the provider’s dashboard.
Client integration
cf-turnstile-response; for reCAPTCHA it’s g-recaptcha-response. All three resolve to a single captchaToken field on the wire to Pylon.
Verify behavior
Missing or invalid token returns400 CAPTCHA_FAILED:
missing-input-response, invalid-input-response) is logged server-side with tracing::warn! so operators can debug from logs without leaking which check failed back to the client.
The gate runs before the rate limiter and before any DB / email work — bots that fail the challenge don’t consume rate-limit budget or trigger downstream actions.
Where to go next
- Magic codes — primary gated endpoint
- Password — register flow uses the same gate
- Phone / SMS —
/phone/send-codeis gated identically