Captcha

Add a bot check

Development 0.2.0.dev · Reviewed 2026-09-07

Add an optional provider check to password sign-in and email-link requests.

Before you start

Without captcha configuration, password and email sign-in work without a provider widget. To enable one, create provider keys for your app’s hostname and store the secret key in your host’s secret store. This page describes the generated integration; verify it with your real provider keys before deployment.

Cloudflare Turnstile

Shell
bin/rails generate add_auth:challenge turnstile

Configure these environment variables with your own values:

Environment
TURNSTILE_SITE_KEY=your-public-site-key
TURNSTILE_SECRET_KEY=your-private-secret-key
TURNSTILE_ALLOWED_HOSTNAMES=accounts.example.com

The generated initializer checks responses on the server and applies the challenge to sign-in and email-link requests. Use a comma-separated hostname list when needed. Turnstile setup documentation covers provider-side configuration.

Google reCAPTCHA

Shell
bin/rails generate add_auth:challenge recaptcha --version=v2

Use RECAPTCHA_SITE_KEY, RECAPTCHA_SECRET_KEY and RECAPTCHA_ALLOWED_HOSTNAMES. Match your provider keys to the chosen v2/v3 mode. See Google’s v2 configuration guide.

Configuration and outages

In production, missing keys or an explicit hostname allowlist prevent the generated provider initializer from booting. Outside production, missing keys leave the provider disabled with a warning.

A rejected check and an unavailable provider are different outcomes. The default outage policy is :closed, which blocks protected actions when the provider cannot be verified. config.challenge_when_unavailable = :open allows them during an outage and is a security tradeoff to evaluate deliberately.

Verify the integration

  1. Run bin/rails add_auth:doctor.
  2. Test password sign-in and an email-link request with real keys for the configured hostname.
  3. Exercise a rejected response and provider unavailability in a safe test environment.
  4. Disable JavaScript and confirm that policy is enforced with an understandable unavailable state. Do not bypass captcha to simulate no-JavaScript support.

Add :reauthenticate to challenge_on if sign-out-everywhere should also require the provider check.

Something unclear? Suggest a correction Release status

Search documentation

Type to find a guide.

Use Tab to move through results. Escape closes search.