PHPackages                             dominservice/invis-captcha - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. dominservice/invis-captcha

ActiveLibrary[Security](/categories/security)

dominservice/invis-captcha
==========================

Invisible, score-based CAPTCHA for Laravel.

1.4.0(1mo ago)032MITPHPPHP ^8.1

Since Aug 6Pushed 2mo agoCompare

[ Source](https://github.com/dominservice/invis-captcha)[ Packagist](https://packagist.org/packages/dominservice/invis-captcha)[ RSS](/packages/dominservice-invis-captcha/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (17)Used By (0)

Invisible CAPTCHA for Laravel
=============================

[](#invisible-captcha-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/e3b81bc7c2dab1672fcb02c99e34dfd6fbfa0e2954931bf357f013296bfdec21/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f646f6d696e736572766963652f696e7669732d636170746368612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dominservice/invis-captcha)[![Total Downloads](https://camo.githubusercontent.com/e8ceeb0505d8421b9f50864c867cd1a2793f2c5fecb05faea9889c4acd3532a6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f646f6d696e736572766963652f696e7669732d636170746368612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dominservice/invis-captcha)[![License](https://camo.githubusercontent.com/a32443f4aacc4dd4d7fef6447c79004f2a36f5f979c7f55522ff2d5c59ebc620/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f646f6d696e736572766963652f696e7669732d636170746368612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dominservice/invis-captcha)

*A zero-UI, score-based anti-bot shield (reCAPTCHA v3-style) with **optional** honey-field, 1-px pixel, polyfill-poisoning, dynamic field names, ML scoring and Cloudflare Turnstile fallback.*

Version Matrix
--------------

[](#version-matrix)

LaravelSupported?Notes**9.x**✅Requires PHP ≥ 8.1**10.x**✅Classic “Kernel + `app/Http`” structure**11.x**✅*New streamlined structure* (no Kernel by default)**12.x**✅Identical to 11 — tested with 12.0.0-betaIf you are **upgrading** an older app to 11/12 you may still keep the classic structure – follow the *≤10* instructions.

---

✨ Features
----------

[](#-features)

ModulePurposeToggle**Invisible scoring**JS collects signals → server returns JWT with `score ∈ [0-1]`.always on**Dynamic field names**Adds random suffixes (e.g. `email_d8e7f3c1`) to fool static parsers.`dynamic_fields.enabled`**Honey field**Hidden input ― if filled → instant block.`honey_field.enabled`**1-px tracking pixel**Logs real browsers vs. lazy headless fetches.`track_pixel.enabled`**Polyfill-Poisoning**Patches browser APIs (e.g. `Canvas.toDataURL()`) to break fingerprint-spoofers.`polyfill_poison.enabled`**Cloudflare Turnstile fallback**Shows Turnstile widget only for low scores.`turnstile.enabled`**Pluggable ML model**Drop-in JSON model for advanced scoring.`ml_model.enabled`---

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require dominservice/invis-captcha
```

After installing, publish the configuration file:

```
php artisan vendor:publish --tag="invis"
```

```
# generates default thresholds model – only when file doesn't exist
php artisan invis:model:generate

# forces overwrite
php artisan invis:model:generate thresholds --force

# variant with linear weights
php artisan invis:model:generate weights
```

Configuration
-------------

[](#configuration)

The configuration file `config/invis.php` allows you to customize:

- Secret key for JWT tokens
- Score threshold for bot detection
- Bypass invis for authenticated users (`skip_authenticated`)
- Honeypot field settings
- Dynamic field name generation
- Cloudflare Turnstile integration
- Tracking pixel options

`INVIS_SECRET` should be at least 32 bytes when using HS256 (required by `firebase/php-jwt` 7.x).

Toggle any module with `true` / `false`:

Framework-specific wiring
-------------------------

[](#framework-specific-wiring)

### Laravel ≤ 10 (classic structure)

[](#laravel--10-classic-structure)

**Register middleware alias**

```
// app/Http/Kernel.php   (inside the $routeMiddleware array)
'verify.invis' => \Dominservice\Invisible\Middleware\Verify::class,
```

**Protect a route**

```
Route::post('/contact', ContactController::class)
     ->middleware('verify.invis');
```

### Laravel ≥ 11 (streamlined structure)

[](#laravel--11-streamlined-structure)

Laravel 11+ uses bootstrap-driven configuration.

```
// bootstrap/app.php  (excerpt)

use Illuminate\Foundation\Configuration\Middleware;
use Dominservice\Invisible\Middleware\Verify;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        // register as ALIAS
        $middleware->alias([
            'verify.invis' => Verify::class,
        ]);
    })
    ->withRoutes(function () {
        require __DIR__.'/../routes/web.php';
    })
    ->create();
```

Protect routes exactly the same way in `routes/web.php`:

```
Route::post('/contact', ContactController::class)
     ->middleware('verify.invis');
```

Basic Usage
-----------

[](#basic-usage)

### 1. Add the Blade directive to your form

[](#1-add-the-blade-directive-to-your-form)

```

    @csrf
    @invisCaptcha

    Submit

```

### 2. Protect your routes with the middleware

[](#2-protect-your-routes-with-the-middleware)

```
// In a route file
Route::post('/submit', 'FormController@submit')->middleware('verify.invis');

// Or in a controller
public function __construct()
{
    $this->middleware('verify.invis');
}
```

How It Works
------------

[](#how-it-works)

1. The `@invisCaptcha` directive adds JavaScript that collects user behavior data
2. When the form is submitted, a score is calculated based on:
    - Mouse movements
    - Keyboard usage
    - Time spent on page
    - Other behavioral signals
3. A JWT token with the score is sent with the form
4. The middleware validates the token and rejects suspicious submissions

Advanced Usage
--------------

[](#advanced-usage)

### Custom Score Threshold

[](#custom-score-threshold)

You can specify a custom score threshold for specific routes:

```
Route::post('/contact', 'ContactController@submit')
    ->middleware('verify.invis:0.7'); // Higher threshold for stricter protection
```

### JavaScript Form Submissions

[](#javascript-form-submissions)

To use the invisible captcha with JavaScript/AJAX form submissions:

1. Add the Blade directive to your page (outside the form):

```
@invisCaptcha
```

2. Add the `data-invis` attribute to your form:

```

```

3. In your JavaScript, wait for the token to be injected before submitting:

### Direct Function Call

[](#direct-function-call)

You can also call the invisCaptcha function directly at any time:

```
// Call with default configuration
window.invisCaptcha().then(result => {
    console.log('Token:', result.token);
    console.log('Score:', result.score);
});

// Or with custom configuration
window.invisCaptcha({
    // Custom configuration options
}).then(result => {
    // Use the token and score
    const { token, score } = result;

    // Add token to your form or request
    document.querySelector('input[name="invis_token"]').value = token;
});
```

This is useful for:

- Single-page applications
- Dynamic form creation
- Custom validation flows
- Refreshing tokens without page reload

### Livewire Form Integration

[](#livewire-form-integration)

To use the invisible captcha with Livewire forms:

1. Add the Livewire-specific Blade directive to your **main layout file** (not inside Livewire components):

```

    Your App
    @livewireStyles

    {{ $slot ?? $content ?? yield('content') }}

    @livewireScripts
    @invisLivewire

```

2. Important placement notes:

    - The `@invisLivewire` directive must be placed in your main layout file, not in individual Livewire component views
    - It should be placed after `@livewireScripts` to ensure Livewire is loaded first
    - You only need to include it once in your main layout
3. Your Livewire forms will be automatically detected and protected. The directive:

    - Automatically adds the `data-invis` attribute to Livewire forms
    - Handles dynamic form updates through Livewire
    - Re-initializes protection after Livewire updates
4. No additional configuration is needed in your Livewire components
5. Ensure your Livewire component's form submission method is protected with the middleware:

```
// in blade
div x-data="{ root: null, mo: null }"
                             x-init="
        root = $el;
        mo = new MutationObserver((mutations) => {
            for (const m of mutations) {
                for (const node of m.addedNodes) {
                    if (!(node instanceof HTMLElement)) continue;

                    const inp = node.matches?.('input[type=hidden][name=invis_token]')
                        ? node
                        : node.querySelector?.('input[type=hidden][name=invis_token]');

                    if (inp) {
                        $wire.set('invis_token', inp.value);
                        inp.addEventListener('input',  () => $wire.set('invis_token', inp.value));
                        inp.addEventListener('change', () => $wire.set('invis_token', inp.value));
                    }
                }
            }
        });
        mo.observe(root, { childList: true, subtree: true });
     ">

// in component
// In your controller that handles the Livewire form submission
public function submit()
{
    // This method needs to be protected with the middleware
        try {
            app(\Dominservice\Invisible\Middleware\Verify::class)
                ->handle(request(), fn () => true);
        } catch (HttpException $e) {
            $this->showVerificationError = true;
        }

    // Your form processing logic
}
```

> **Note:** The middleware automatically detects and processes Livewire requests. It will look for tokens and form fields in both regular POST data and Livewire component data. The middleware supports both the standard Livewire format and the newer format with `snapshot` (JSON string) and `updates` fields. This means you don't need any special configuration to use the middleware with Livewire forms - it just works!

### Troubleshooting Livewire Integration

[](#troubleshooting-livewire-integration)

If the Livewire integration is not working:

1. Make sure the `@invisLivewire` directive is placed in your main layout file, not in individual Livewire components
2. Verify that it comes AFTER `@livewireScripts` in your HTML
3. Check your browser console for any JavaScript errors
4. Verify that your forms have Livewire attributes (wire:submit, wire:model, etc.)
5. Make sure you've published the package assets: `php artisan vendor:publish --tag="invis"`
6. Check that the middleware is properly registered and applied to your form submission handler

```
document.getElementById('submitButton').addEventListener('click', async function(e) {
    e.preventDefault();

    // Wait for the token to be injected (if not already)
    if (!document.querySelector('input[name="invis_token"]')) {
        await new Promise(resolve => setTimeout(resolve, 500));
    }

    const form = document.getElementById('myForm');
    const formData = new FormData(form);

    // Send with fetch
    fetch('/your-endpoint', {
        method: 'POST',
        body: formData,
        credentials: 'same-origin'
    })
    .then(response => response.json())
    .then(data => {
        // Handle response
    })
    .catch(error => {
        // Handle error
    });

    // Or with axios
    // axios.post('/your-endpoint', formData)
    //     .then(response => { /* Handle response */ })
    //     .catch(error => { /* Handle error */ });
});
```

4. Ensure your endpoint is protected with the middleware:

```
Route::post('/your-endpoint', 'YourController@handle')
    ->middleware('verify.invis');
```

### Cloudflare Turnstile Integration

[](#cloudflare-turnstile-integration)

Enable Turnstile in your config file and add your site and secret keys:

```
'turnstile' => [
    'enabled' => true,
    'sitekey' => 'your-site-key',
    'secret' => 'your-secret-key',
    'fallback' => 0.30,
],
```

Translations
------------

[](#translations)

The package includes translations for error messages in English and Polish. You can publish the translation files to customize them:

```
php artisan vendor:publish --tag="invis-translations"
```

This will publish the translation files to `resources/lang/vendor/invis/` where you can edit them or add new languages.

### Available Error Messages

[](#available-error-messages)

The following error messages are available for translation:

- `honey_field` - Displayed when a bot fills the honey field
- `missing_token` - Displayed when the token is missing from the request
- `invalid_token` - Displayed when the token is invalid
- `token_expired` - Displayed when the token has expired
- `invalid_signature` - Displayed when the token signature is invalid
- `ip_mismatch` - Displayed when the IP address doesn't match
- `score_too_low` - Displayed when the score is below the threshold
- `turnstile_error` - Displayed when there's an error with Turnstile verification

### Adding a New Language

[](#adding-a-new-language)

To add a new language, create a new directory in `resources/lang/vendor/invis/` with your language code (e.g., `de` for German) and copy the structure from the English files.

Testing
-------

[](#testing)

```
composer test
```

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

Credits
-------

[](#credits)

- [dominservice](https://github.com/dominservice)
- [All Contributors](../../contributors)

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance86

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~16 days

Recently: every ~54 days

Total

16

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9d67c041316385020aafb7eef9f11971d6fad80a11a44f4078273bda6890722d?d=identicon)[dominservice](/maintainers/dominservice)

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/dominservice-invis-captcha/health.svg)

```
[![Health](https://phpackages.com/badges/dominservice-invis-captcha/health.svg)](https://phpackages.com/packages/dominservice-invis-captcha)
```

###  Alternatives

[illuminate/encryption

The Illuminate Encryption package.

9229.7M280](/packages/illuminate-encryption)[tzsk/otp

A secure, database-free One-Time Password (OTP) generator and verifier for PHP and Laravel.

241641.4k1](/packages/tzsk-otp)[genealabs/laravel-governor

Managing policy and control in Laravel.

201262.8k](/packages/genealabs-laravel-governor)[dgtlss/warden

A Laravel package that proactively monitors your dependencies for security vulnerabilities by running automated composer audits and sending notifications via webhooks and email

8745.6k](/packages/dgtlss-warden)[leuchtfeuer/secure-downloads

"Secure Download": Apply TYPO3 access rights to ALL file assets (PDFs, TGZs or JPGs etc. - configurable) - protect them from direct access.

22234.7k1](/packages/leuchtfeuer-secure-downloads)[ercsctt/laravel-file-encryption

Secure file encryption and decryption for Laravel applications

642.6k](/packages/ercsctt-laravel-file-encryption)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
