danielm/laravel-simple-altcha
| Install | |
|---|---|
composer require danielm/laravel-simple-altcha |
|
| Latest Version: | v0.1.5 |
| PHP: | ^8.2 |
| License: | MIT |
| Last Updated: | Oct 8, 2026 |
| Links: | GitHub · Packagist |
Laravel ALTCHA
ALTCHA proof-of-work captcha for Laravel, built on
altcha-org/altcha v2. Includes:
- a challenge endpoint (
GET /altcha/challenge) - a
ValidAltchavalidation rule and analtcharoute middleware - replay protection (a solved payload works once)
- a React / Inertia component published as a stub
Install
composer require danielm/laravel-simple-altcha
npm install altcha
.env:
# php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"
ALTCHA_HMAC_SECRET=
All supported variables are documented in .env.example.
Optional publishing:
php artisan vendor:publish --tag=altcha-config # config/altcha.php
php artisan vendor:publish --tag=altcha-lang # translations
php artisan vendor:publish --tag=altcha-react # React component + JSX types
The altcha-react tag copies:
resources/js/components/altcha-widget.tsx
Backend
Validation rule (implicit, so a missing field also fails):
use Danielm\LaravelSimpleAltcha\Rules\ValidAltcha;
$request->validate([
'email' => ['required', 'email'],
'altcha' => [new ValidAltcha],
]);
Or middleware:
Route::post('/contact', ContactController::class)->middleware('altcha');
Or directly:
$result = app(\Danielm\LaravelSimpleAltcha\AltchaManager::class)->verify($request->input('altcha'));
$result->verified; // bool
$result->reason; // missing | malformed | expired | invalid | replayed
Or global middleware (in bootstrap/app.php):
$middleware->web(append: [
//... other laravel middlewares
VerifyGlobalAltcha::class,
]);
In this case you must also define ALTCHA_VERIFY_ROUTES to specify what route (names) to check:
ALTCHA_VERIFY_ROUTES=login.store,register.store
Frontend (Inertia + React)
import { useRef } from 'react';
import { useForm } from '@inertiajs/react';
import AltchaWidget, { AltchaHandle } from '@/components/altcha-widget';
export default function Contact() {
const altcha = useRef<AltchaHandle>(null);
const { data, setData, post, processing, errors } = useForm({ email: '', altcha: '' });
const submit = (e: React.FormEvent) => {
e.preventDefault();
post('/contact', {
// payloads are single-use, so always get a fresh one afterwards
onFinish: () => altcha.current?.reset(),
});
};
return (
<form onSubmit={submit}>
<input value={data.email} onChange={(e) => setData('email', e.target.value)} />
<AltchaWidget ref={altcha} onChange={(payload) => setData('altcha', payload)} />
{errors.altcha && <p>{errors.altcha}</p>}
<button disabled={processing || !data.altcha}>Send</button>
</form>
);
}
challengeUrl defaults to /altcha/challenge. If you change altcha.route.path,
pass the new URL. Extra props are forwarded to <altcha-widget> as attributes.
Configuration notes
- Algorithm:
pbkdf2(default, no extensions),argon2id(ext-sodium),scrypt(ext-scrypt).costmeans different things per algorithm. Your widget build must support the one you choose. - Replay protection uses
Cache::add(). Use redis/memcached/database in production; thefiledriver is not atomic. Keys are derived from the challenge's HMAC signature, so re-encoding a payload does not bypass it. - Challenge route is intentionally outside the
webgroup (no session or cookies) and throttled by default. Adjustaltcha.route.middleware. - Widget requires HTTPS (localhost is fine for development).
Testing in your app
Set ALTCHA_TESTING_BYPASS=let-me-in in .env.testing, then post
['altcha' => 'let-me-in']. The bypass only works when the app environment
is testing.
Package tests
composer install
composer test
Related Packages
Cloudflare Turnstile integration for Laravel with support for React, Inertia.js...
A reusable, server-side DataTable system for Laravel + Inertia.js + React (TanSt...
Illuminate your API — interactive inspector for Laravel 12 with Inertia + React
Domion - Professional Architecture for Laravel with Automated Frontend & Auth In...
Starter Kit Laravel 12 + React + Inertia + Tailwind + ShadCN UI + Spatie Permiss...
Version History
| Version | Released | PHP | Laravel | License |
|---|---|---|---|---|
| v0.1.5 | ^8.2 | ^12.0| | MIT | |
| v0.1.4 | ^8.2 | ^12.0| | MIT | |
| v0.1.3 | ^8.2 | ^12.0| | MIT | |
| v0.1.2 | ^8.2 | ^12.0| | MIT | |
| v0.1.1 | ^8.2 | ^12.0| | MIT | |
| v0.1.0 | ^8.2 | ^12.0| | MIT |