Download the PHP package azymuthia/turnstile-bundle without Composer
On this page you can find all versions of the php package azymuthia/turnstile-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package turnstile-bundle
Turnstile bundle
Strict Cloudflare Turnstile verification for Symfony firewalls and forms.
- Refuses whenever it cannot verify. A timeout, a transport error, a non-2xx answer or a malformed one means the check failed, never that it passed.
- Checks more than
success. The token must have been issued on the request host, or on a hostname you allow, for the action you expect. The visitor's IP is sent to Cloudflare, and the request is bounded to 5 seconds idle and 10 seconds in total. - Plugs into the firewall.
form_loginkeeps its CSRF check, login throttling and user checker. The Turnstile check runs after CSRF and throttling and before the user is loaded or the password hashed, so a bot costs no database lookup or password hash. - Has no off switch. Tests run with Turnstile on, against a mock of Cloudflare's answer.
Requires PHP 8.3 or later and Symfony 7.4 or 8.
Installation
Add the bundle to config/bundles.php if Flex did not:
Create a widget in the Cloudflare dashboard (Turnstile → Add widget). Use one widget per application and environment, and Managed mode unless you have a reason to pick another. Then configure the bundle:
The visitor's IP behind Cloudflare
Behind Cloudflare or another proxy, Request::getClientIp() is the proxy's address unless the proxy is
a trusted proxy. If it is not, implement
VisitorIpResolver and name the service in visitor_ip_resolver:
Login (form_login)
List the firewall under firewalls and render the widget inside the login form:
azymuthia_turnstile_widget(action, options) accepts field_name (default cf-turnstile-response),
theme (auto, light or dark; default auto), size (flexible, normal or compact; default
flexible) and language (default: the request locale). The action must match the firewall's action,
and the field name must match its field.
A refused login throws Azymuthia\TurnstileBundle\Security\TurnstileAuthenticationException. Its message
key, Verification failed. Refresh the page and try again., is translated in the security domain and
is the same whether the check failed or Cloudflare could not be reached. getErrorCodes() tells the two
apart, for a LoginFailureEvent listener to log:
A Turnstile failure counts towards login throttling like any other failed login.
Only form_login is covered: remember-me, HTTP basic and other authenticators on the same firewall are
left alone. A custom authenticator can require a token itself, on any firewall, by adding a badge to
its passport:
Forms
The field renders the widget and validates the token with the Turnstile constraint. The violation
message is translated in the validators domain. The field is not mapped. Options:
| Option | Default | Meaning |
|---|---|---|
turnstile_action |
(required) | The widget's action, which the token must match. |
turnstile_validation |
true |
Validate the token when the form is submitted. |
turnstile_theme |
auto |
auto, light or dark. |
turnstile_size |
flexible |
flexible, normal or compact. |
turnstile_language |
request locale | The widget's language. |
The widget is rendered by the turnstile_widget block, which the bundle adds to twig.form_themes.
Verifying later
Tokens are single-use. If something has to be checked before Turnstile, for example a rate limit, render
the field with 'turnstile_validation' => false and verify the token yourself afterwards:
TurnstileVerifier is a public service (also azymuthia_turnstile.verifier).
Error codes
A refused check carries Cloudflare's error codes,
sanitized, or one of these (constants on Azymuthia\TurnstileBundle\Verifier\ErrorCode):
| Code | Meaning |
|---|---|
unreachable |
Siteverify timed out, could not be reached, or answered non-2xx or malformed. |
missing-input-response |
No token was posted. |
invalid-input-response |
The token is longer than any Turnstile token. |
invalid-input-secret |
The secret is empty, or is a dummy secret in prod. |
hostname-mismatch |
The token was issued on another hostname. |
action-mismatch |
The token was issued for another action. |
Tokens and secrets are never logged. The bundle logs on the turnstile channel when Monolog is installed.
Development: Cloudflare's dummy keys
Cloudflare publishes dummy keys
for development. The secrets 1x0000000000000000000000000000000AA (always passes),
2x0000000000000000000000000000000AA (always fails) and 3x0000000000000000000000000000000AA (token
already spent) answer with the hostname localhost and the action test. For these secrets alone the
hostname and action match is skipped, so the dummy site keys work on any development host:
When kernel.environment is prod, a dummy secret refuses every check and logs at critical. An
always-passing secret in production would silently switch Turnstile off. Set the real keys in
production.
Testing your application
The bundle never calls Cloudflare in tests if you swap its HTTP client, azymuthia_turnstile.http_client,
for a MockHttpClient answering through TurnstileMockResponseFactory:
TurnstileMockResponseFactory::token($action) returns a token that passes for that action. Anything else
is refused with invalid-input-response, as Cloudflare would refuse it:
The response's hostname is localhost, the test client's default. For another host, pass it to the
factory: arguments: ['app.example.com']. Use a secret that is not one of the dummy secrets in test
(e.g. CLOUDFLARE_TURNSTILE_SECRET_KEY=test-secret in .env.test). Otherwise the hostname and action
are not checked, and a token for the wrong action would pass.
Licence
MIT, see LICENSE.
All versions of turnstile-bundle with dependencies
psr/log Version ^2.0 || ^3.0
symfony/config Version ^7.4 || ^8.0
symfony/dependency-injection Version ^7.4 || ^8.0
symfony/form Version ^7.4 || ^8.0
symfony/framework-bundle Version ^7.4 || ^8.0
symfony/http-client Version ^7.4 || ^8.0
symfony/http-client-contracts Version ^3.4
symfony/http-foundation Version ^7.4 || ^8.0
symfony/http-kernel Version ^7.4 || ^8.0
symfony/options-resolver Version ^7.4 || ^8.0
symfony/security-bundle Version ^7.4 || ^8.0
symfony/security-core Version ^7.4 || ^8.0
symfony/security-http Version ^7.4 || ^8.0
symfony/twig-bundle Version ^7.4 || ^8.0
symfony/uid Version ^7.4 || ^8.0
symfony/validator Version ^7.4 || ^8.0
twig/twig Version ^3.12