afatmustafa/filamentv3-turnstile
Installation
composer require afatmustafa/filamentv3-turnstile
Publish the config file (if needed):
php artisan vendor:publish --provider="Afatmustafa\Filamentv3Turnstile\Filamentv3TurnstileServiceProvider" --tag="config"
Configure Cloudflare Turnstile
Add your Turnstile site key and secret to .env:
TURNSTILE_SITE_KEY=your_site_key
TURNSTILE_SECRET_KEY=your_secret_key
First Use Case: Add to a Form
Attach the widget to a Filament form (e.g., Create or Edit resource):
use Afatmustafa\Filamentv3Turnstile\Widgets\Turnstile;
public static function form(Form $form): Form
{
return $form
->schema([
// ... other fields
Turnstile::make('turnstile')
->label('Verify You Are Human')
->required(),
]);
}
Verify in Blade (if needed)
For non-Filament forms, use the underlying laravel-turnstile package:
@turnstile(['sitekey' => config('services.turnstile.site_key')])
config/filamentv3-turnstile.php):
'validation' => [
'rule' => 'required|turnstile',
'message' => 'Please complete the Turnstile challenge.',
],
Override the site key per form or widget:
Turnstile::make('turnstile')
->siteKey('custom_site_key_123')
->label('Custom Challenge'),
Restrict access to forms requiring Turnstile:
public static function canAccess(): bool
{
return auth()->user()->hasRole('admin'); // Example condition
}
Pass options to the underlying Turnstile script:
Turnstile::make('turnstile')
->options([
'theme' => 'light',
'size' => 'compact',
]),
Redirect or show a message on validation failure:
public function rules(): array
{
return [
'turnstile' => ['required', 'turnstile'],
];
}
public function messages(): array
{
return [
'turnstile.turnstile' => 'Verification failed. Please try again.',
];
}
Use the underlying package's middleware for API routes:
Route::middleware(['turnstile.verify'])->group(function () {
// Protected routes
});
Mock Turnstile responses in tests:
$this->actingAs($user)
->post('/form', [
'turnstile' => 'mock_success_token',
])
->assertRedirect('/success');
Localize Turnstile labels and messages:
Turnstile::make('turnstile')
->label(__('filament::resources.pages.verify-human')),
.env ConfigurationTURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY are set in .env.config('services.turnstile') in Tinker to verify keys.Access-Control-Allow-Origin errors.document.getElementById('turnstile-widget').reload();
'auto_register_assets' => false,
Then manually include the script in your layout:
@vite(['resources/js/turnstile.js'])
php artisan filament:cache-clear
Add to .env:
TURNSTILE_DEBUG=true
This logs verification responses to Laravel logs.
F12) and check the POST /api/turnstile/verify request.response token is sent correctly.Ensure the secret key in .env matches the one in your Cloudflare Turnstile dashboard.
console.log('Turnstile loaded:', typeof turnstile !== 'undefined');
Extend the validation rule in app/Providers/AppServiceProvider.php:
use Illuminate\Support\Facades\Validator;
Validator::extend('custom_turnstile', function ($attribute, $value, $parameters, $validator) {
// Custom logic (e.g., IP-based checks)
return true; // or false
});
Create a custom widget class:
namespace App\Filament\Widgets;
use Afatmustafa\Filamentv3Turnstile\Widgets\Turnstile;
class CustomTurnstile extends Turnstile
{
protected string $defaultOptions = [
'theme' => 'dark',
'callback' => 'customCallback',
];
}
Combine with Laravel Honeypot for extra security:
use App\Filament\Widgets\CustomTurnstile;
Turnstile::make('turnstile')
->required()
->afterStateUpdated(function (Turnstile $widget, ?string $state) {
if (!$state) {
$widget->addError('turnstile', 'Please complete the challenge.');
}
}),
Extend the Turnstile class to log attempts:
public function handleVerification(string $response)
{
\Log::info('Turnstile verification attempt', ['response' => $response]);
return parent::handleVerification($response);
}
Skip Turnstile for trusted users (e.g., admins):
Turnstile::make('turnstile')
->visible(fn () => !auth()->user()->isAdmin()),
How can I help you explore Laravel packages today?