-
Notifications
You must be signed in to change notification settings - Fork 1
Turnstile #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Turnstile #69
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request integrates Cloudflare Turnstile CAPTCHA into authentication, registration, and lessee creation flows to protect against automated abuse. The implementation introduces custom request classes for cleaner validation, a reusable Turnstile validation rule, and a helper class for server-side token verification. Frontend views are updated to conditionally display the Turnstile widget based on configuration.
Key changes include:
- Addition of Cloudflare Turnstile configuration with feature toggle support
- Refactoring of authentication and registration to use dedicated request classes
- Implementation of server-side Turnstile validation with appropriate error handling
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| config/services.php | Adds Cloudflare Turnstile configuration (enabled flag, site key, secret key) |
| phpunit.xml | Disables Turnstile for tests to avoid validation during automated testing |
| app/Helpers/TurnstileHelper.php | Implements server-side validation of Turnstile tokens via Cloudflare API |
| app/Rules/TurnstileRule.php | Provides reusable validation rule for Turnstile token verification |
| app/Http/Requests/LoginRequest.php | Extracts login validation logic with conditional Turnstile validation |
| app/Http/Requests/RegisterRequest.php | Extracts registration validation logic with conditional Turnstile validation |
| app/Http/Requests/LesseeRequest.php | Adds Turnstile validation to lessee creation form |
| app/Http/Controllers/AuthController.php | Updates to use LoginRequest for cleaner validation |
| app/Http/Controllers/Auth/RegisterController.php | Refactors to use RegisterRequest and simplifies user creation |
| resources/views/auth/login.blade.php | Adds conditional Turnstile widget display with error handling |
| resources/views/auth/register.blade.php | Adds conditional Turnstile widget display with error handling |
| resources/views/catalogos/arrendatario/create.blade.php | Adds conditional Turnstile widget to lessee creation form |
| resources/views/layouts/app.blade.php | Adds @stack directive for dynamic script injection |
| resources/views/layouts/layout-v2.blade.php | Adds @stack directive and improves user menu with logout button |
| routes/web.php | Adds named route for logout functionality |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| return $rules; | ||
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The LesseeRequest is missing custom error messages for the Turnstile validation, unlike LoginRequest and RegisterRequest which provide user-friendly Spanish messages. Consider adding a messages() method to provide consistent, localized error messages for the 'cf-turnstile-response.required' validation.
| } | |
| } | |
| /** | |
| * Get custom validation messages for this request. | |
| * | |
| * @return array | |
| */ | |
| public function messages() | |
| { | |
| return [ | |
| 'cf-turnstile-response.required' => 'Por favor, verifica que no eres un robot.', | |
| ]; | |
| } |
| $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ | ||
| 'secret' => $secretKey, | ||
| 'response' => $token, | ||
| 'remoteip' => $ip ?? request()->ip(), | ||
| ]); |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTTP request to Cloudflare Turnstile API lacks timeout configuration. If the Cloudflare API is slow or unresponsive, this could cause the authentication/registration process to hang indefinitely. Consider adding a timeout to the HTTP request to prevent blocking users for extended periods.
| $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ | |
| 'secret' => $secretKey, | |
| 'response' => $token, | |
| 'remoteip' => $ip ?? request()->ip(), | |
| ]); | |
| $response = Http::asForm() | |
| ->timeout(5) | |
| ->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ | |
| 'secret' => $secretKey, | |
| 'response' => $token, | |
| 'remoteip' => $ip ?? request()->ip(), | |
| ]); |
| $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ | ||
| 'secret' => $secretKey, | ||
| 'response' => $token, | ||
| 'remoteip' => $ip ?? request()->ip(), | ||
| ]); | ||
|
|
||
| $result = $response->json(); | ||
|
|
||
| if (!$response->successful() || !isset($result['success'])) { | ||
| Log::warning('Turnstile validation request failed', [ | ||
| 'status' => $response->status(), | ||
| 'body' => $response->body(), | ||
| ]); | ||
| return false; | ||
| } | ||
|
|
||
| return $result['success'] === true; |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling for failed Turnstile validation could be improved. When the HTTP request fails or returns an unexpected response, only a generic log warning is created. Consider differentiating between network failures, API errors, and validation failures to provide better debugging information and potentially different user experiences.
| $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ | |
| 'secret' => $secretKey, | |
| 'response' => $token, | |
| 'remoteip' => $ip ?? request()->ip(), | |
| ]); | |
| $result = $response->json(); | |
| if (!$response->successful() || !isset($result['success'])) { | |
| Log::warning('Turnstile validation request failed', [ | |
| 'status' => $response->status(), | |
| 'body' => $response->body(), | |
| ]); | |
| return false; | |
| } | |
| return $result['success'] === true; | |
| try { | |
| $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ | |
| 'secret' => $secretKey, | |
| 'response' => $token, | |
| 'remoteip' => $ip ?? request()->ip(), | |
| ]); | |
| } catch (\Exception $e) { | |
| Log::error('Turnstile validation network or client error', [ | |
| 'message' => $e->getMessage(), | |
| 'exception' => get_class($e), | |
| 'ip' => $ip ?? request()->ip(), | |
| ]); | |
| return false; | |
| } | |
| if (!$response->successful()) { | |
| Log::warning('Turnstile validation API responded with error status', [ | |
| 'status' => $response->status(), | |
| 'body' => $response->body(), | |
| ]); | |
| return false; | |
| } | |
| $result = $response->json(); | |
| if (!is_array($result) || !array_key_exists('success', $result)) { | |
| Log::warning('Turnstile validation response missing expected "success" field', [ | |
| 'status' => $response->status(), | |
| 'body' => $response->body(), | |
| ]); | |
| return false; | |
| } | |
| if ($result['success'] !== true) { | |
| Log::info('Turnstile validation failed', [ | |
| 'status' => $response->status(), | |
| 'error_codes' => $result['error-codes'] ?? null, | |
| ]); | |
| return false; | |
| } | |
| return true; |
|
|
||
| use App\Rules\TurnstileRule; | ||
| use Illuminate\Foundation\Http\FormRequest; | ||
| use Illuminate\Validation\Rules\Password; |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Password validation rule is imported but not used. The password validation currently uses simple string rules ('required', 'string', 'min:8', 'confirmed'). Either remove this unused import or consider using Laravel's Password rule object for more robust password validation (e.g., Password::min(8) with additional requirements).
| Log::warning('Cloudflare Turnstile secret key is not configured'); | ||
| return false; |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When Turnstile is enabled but the secret key is not configured, the validation silently fails for all users attempting to login, register, or create lessees. This creates a poor user experience as users receive generic validation errors without understanding the root cause. Consider throwing a configuration exception in non-production environments or logging an error-level message (instead of warning) to make configuration issues more visible during development and deployment.
| Log::warning('Cloudflare Turnstile secret key is not configured'); | |
| return false; | |
| if (app()->environment('production')) { | |
| Log::error('Cloudflare Turnstile secret key is not configured'); | |
| return false; | |
| } | |
| throw new \RuntimeException('Cloudflare Turnstile secret key is not configured.'); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on February 9
Details
You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
| @section('javascript') | ||
| @if(config('services.cloudflare.turnstile', false)) | ||
| <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> | ||
| @endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turnstile script breaks Vue initialization in lessee form
High Severity
The Turnstile <script src=""> tag is placed inside @section('javascript') which contains raw JavaScript code (Vue initialization). When @yield('javascript') outputs this section in layout-v2.blade.php, the HTML script tag is rendered followed by raw JavaScript that isn't wrapped in <script> tags. This causes the Vue code to be displayed as plain text rather than executed, breaking the form's image preview functionality when Turnstile is enabled. The fix is to use @push('scripts') like the other views (login.blade.php and register.blade.php) do.
| ]; | ||
|
|
||
| if (config('services.cloudflare.turnstile', false)) { | ||
| $rules['cf-turnstile-response'] = ['required', new TurnstileRule()]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing string validation causes TypeError crash on array input
Medium Severity
The cf-turnstile-response validation rules are missing a 'string' validator. When a malicious request submits this field as an array (e.g., cf-turnstile-response[]=value), the array passes both the required check and TurnstileRule's empty() check, then gets passed to TurnstileHelper::validate() which has a string $token type hint. This causes a TypeError crash (500 error) instead of returning a proper validation error. Other fields like password correctly include 'string' in their rules.
Additional Locations (2)
|
|
||
| if (config('services.cloudflare.turnstile', false)) { | ||
| $rules['cf-turnstile-response'] = ['required', new TurnstileRule()]; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit form missing Turnstile widget breaks updates
High Severity
The LesseeRequest class is used for both store and update controller methods, and now requires cf-turnstile-response when Turnstile is enabled. However, only create.blade.php was updated with the Turnstile widget—edit.blade.php was not modified. When Turnstile is enabled, any attempt to update a lessee record will fail validation because the edit form doesn't include the required cf-turnstile-response field, effectively breaking all lessee edits.
| } | ||
|
|
||
| // Validar el token con Cloudflare | ||
| if (!TurnstileHelper::validate($value)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing type validation causes TypeError on array input
Medium Severity
The TurnstileRule::validate() method receives a mixed $value and passes it directly to TurnstileHelper::validate() which expects a string. If a user manipulates the form to submit cf-turnstile-response as an array, the empty() check passes for non-empty arrays, and PHP 8 throws a TypeError when attempting to pass the array to the helper. The validation rules lack a 'string' validator to ensure type safety before the custom rule executes.
This pull request adds Cloudflare Turnstile CAPTCHA integration to the authentication and registration flows, as well as to the lessee creation form, to enhance security against automated abuse. It refactors request validation by introducing custom request classes and a reusable validation rule for Turnstile. The views are updated to conditionally display the Turnstile widget when enabled via configuration.
Cloudflare Turnstile Integration
config/services.phpandphpunit.xml, enabling feature toggling and key management. [1] [2]TurnstileHelperandTurnstileRulefor validating Turnstile tokens server-side. [1] [2]Authentication and Registration Refactor
LoginRequestandRegisterRequestclasses to handle validation, including Turnstile when enabled, with custom error messages. [1] [2]AuthControllerandRegisterControllerto use these new request classes and simplified user creation logic. [1] [2] [3] [4] [5]Lessee Creation Form Security
LesseeRequestand updated the lessee creation view to display the widget and handle errors. [1] [2] [3] [4] [5]Frontend Updates for Turnstile
Note
Implements optional Cloudflare Turnstile protection and streamlines validation while improving CI/Dusk reliability.
TurnstileHelperand reusableTurnstileRule; addscf-turnstile-responsechecks (toggled viaconfig/services.php) inLoginRequest,RegisterRequest, andLesseeRequestAuthControllerandRegisterControllernow useLoginRequest/RegisterRequest; simplifies user creation; changeslogouttoPOSTwith named routelogoutinroutes/web.phpauth/login.blade.php,auth/register.blade.php, and lessee create view conditionally render Turnstile widget and errors; layouts add@stack('scripts')and improve user menu/logoutAPP_URL;DuskTestCaseusesRefreshDatabase, ensures SQLite file, and updates headless Chrome flags; phpunit configs set SQLite and disable Turnstile by defaultdown()by throwingRuntimeExceptionWritten by Cursor Bugbot for commit 38a380e. This will update automatically on new commits. Configure here.