klangch / laravel-ir-captcha
Image rotation captcha library for Laravel 11/12
Requires
- php: ^8.1
- ext-gd: *
- ext-json: *
- illuminate/config: ^11|^12
- illuminate/contracts: ^11|^12
- illuminate/filesystem: ^11|^12
- illuminate/session: ^11|^12
- illuminate/support: ^11|^12
- intervention/image-laravel: ^1.5
Requires (Dev)
- mockery/mockery: ^1.0
- phpunit/phpunit: ^11
This package is auto-updated.
Last update: 2025-05-07 17:11:48 UTC
README
Laravel IR Captcha (Image Rotation Captcha) is a customizable image rotation CAPTCHA package for Laravel 11 and 12.
Features
- Interactive, intuitive, and more user-friendly than classic text-based CAPTCHAs.
- Customizable UI and flexible configuration.
- No third-party services required. Everything are handled locally.
Preview
LaravelIRCaptcha.Demo.mp4
Installation
Make sure the PHP gd
extension is installed and enabled.
Install with composer using following command:
composer require klangch/laravel-ir-captcha
Configuration
Publish configuration, view, localization files by using following command:
$ php artisan vendor:publish --provider="Klangch\LaravelIRCaptcha\LaravelIRCaptchaServiceProvider"
config/ir-captcha.php
lang/vendor/ir-captcha/en/messages.php
resoures/views/vendor/ir-captcha/irCaptcha.blade.php
Usage
Show captcha in iframe
<iframe src="{{ ir_captcha()->iframeUrl() }}" height="290" width="300"></iframe>
Listen for iframe's post message
window.addEventListener("message", (event) => { if (event.data && event.data.type === "irCaptcha" && event.data.status === "success") { // Get captcha token and set to input field document.querySelector("input[name='captcha_token']").value = event.data.captchaToken; // Continue form submission... } });
URL for iframe
// Get captcha URL with helper ir_captcha()->iframeUrl(); // Get captcha URL with parent origin and dark theme ir_captcha()->iframeUrl('https://[your_parent_origin].com', true); // Or append parent origin and dark theme param manually $url = ir_captcha()->iframeUrl() . '?parent_origin=https://[your_parent_origin].com&theme=dark';
or set dark theme dynamically in Laravel Blade + JS
// Get captcha URL with helper let captchaUrl = "{{ ir_captcha()->iframeUrl() }}"; // Or without helper let captchaUrl = "https://[your_captcha_domain].com/ir-captcha"; // Then append parent origin captchaUrl += "?parent_origin=" + window.location.origin; // Append theme if (themeMode === "dark") { captchaUrl += "&theme=dark"; } document.getElementById("captchaIframe").src = captchaUrl;
Validate captcha token
if (ir_captcha()->validateCaptchaToken($token) === true) { // Validation success }
or by using Laravel validation rule:
use Illuminate\Support\Facades\Validator; use Klangch\LaravelIRCaptcha\Rules\IRCaptcha; $rules = [ 'captcha_token' => ['required', new IRCaptcha], ]; $validator = Validator::make($request->all(), $rules); $validator->validate();
Clear expired captcha files
Clear expired captcha files by using following command:
$ php artisan ir-captcha:clear-expired
You can set this command in cron job to regularly clear expired files.
⚠️ Note For Frontend Framework On Different Domains ⚠️
When using this package with frontend hosted on different domain (such as a Next.js app embedding captcha verification page via an iframe), there are a few important things to keep in mind:
⚠️ CSRF Exclusion
CSRF protection may block requests due to cross-origin restrictions. To allow verification requests from the iframe to bypass CSRF validation, you can explicitly exclude the verification endpoint:
// bootstrap/app.php ->withMiddleware(function (Middleware $middleware) { // ... $middleware->validateCsrfTokens(except: [ 'ir-captcha-verify', ]); });
⚠️ Set Captcha Locale
You can set the captcha's language by passing a locale code as a query string parameter, such as _lang
. Then, use middleware to set the application's locale accordingly.
For example, in your HTML:
<iframe src="https://[your_captcha_domain].com/ir-captcha?parent_origin=https://[your_parent_origin].com&_lang=cn"></iframe>
In your middleware, retrieve the _lang
value and set the application locale:
public function handle(Request $request, Closure $next): Response { $locale = $request->input('_lang', 'en'); App::setLocale($locale); return $next($request); }
For a complete working example, see the Code Example repository.