captchaapi/laravel
Official Laravel SDK for captchaapi.eu (EU-hosted, GDPR-friendly proof-of-work CAPTCHA). Includes Blade widget/component, server-side verification + validation rule, and optional Livewire 4 support. PHP 8.2+, Laravel 12/13.
FakeCaptchaapi::enforceSingleUse() — opts the fake into the server's
single-use contract. A response value verifies once; the per-request memo
still lets Fortify's double validation through, but a replay in a later
request is rejected. Lets integration tests prove a login survives Fortify's
two captcha validations instead of silently bypassing them. The default fake
is unchanged and still accepts any value.CAPTCHAAPI_SECRET_KEY (was
CAPTCHAAPI_SECRET), the config key to secret_key (was secret), and the
accessor to Captchaapi::secretKey() (was secret()), to match the existing
CAPTCHAAPI_SITE_KEY / site_key / siteKey(). Update .env:
CAPTCHAAPI_SECRET=... becomes CAPTCHAAPI_SECRET_KEY=....ValidCaptcha. Frameworks like
Fortify run the validator twice in a single request. Server-side
verification is single-use, so the second pass hit an already-consumed token
and returned invalid_token, rejecting a visitor who had passed the first
time — every Fortify login and registration failed. The rule now memoizes a
success per request and short-circuits the repeat call, exactly as it did
before 3.0 (when the single-use guard was a local jti cache).A breaking release: verification moved from a local HMAC check to a server-side call, matching how every hosted CAPTCHA works and keeping the secret off the browser.
ValidCaptcha now verifies server-side. It posts the widget's
response to captchaapi.eu /verify with your secret as a Bearer token and
accepts the submission only when the server returns success, instead of
validating a signed attestation in PHP. One outbound call per submit, with a
single attempt — the response is single-use, so a retry would spend a token
the visitor already solved.
The form field is now captchaapi_response (was captcha_attestation),
matching the value the widget injects. The Blade components and the
WithCaptcha trait are updated; update your own validation keys and any
hand-written hidden input or [@error](https://github.com/error)('captcha_attestation') directive.
fail_open config (CAPTCHAAPI_FAIL_OPEN, default true) — decides what
happens when the verify call can't reach a verdict (server unreachable or a
5xx). The default lets the submission through; set it false for sensitive
actions, where the visitor is asked to try again rather than told they failed.timeout config (CAPTCHAAPI_VERIFY_TIMEOUT, default 5) — seconds to
wait for the verify call before applying the fail policy.replay_protection, cache_prefix, and
clock_skew_leeway. The server owns single-use now: a response verifies
exactly once.CAPTCHAAPI_SECRET_KEYS list, replaced by a single CAPTCHAAPI_SECRET.
Rotation is handled in the dashboard, which keeps the previous secret valid
during the overlap.illuminate/cache dependency, which only backed replay tracking; added
illuminate/http for the verify call.CAPTCHAAPI_SECRET_KEYS with a single CAPTCHAAPI_SECRET.captcha_attestation to captchaapi_response in your
validation rules, any direct $this->captcha_attestation references, and any
hand-written markup. The shipped Blade components already use the new name.CAPTCHAAPI_REPLAY_PROTECTION and CAPTCHAAPI_CLOCK_SKEW_LEEWAY from
.env — they no longer do anything.CAPTCHAAPI_FAIL_OPEN=false on login or payment forms.Re-tagged 2.1.4 to correct the release. No code changes.
captchaapi.enabled is now honored in the Livewire path. The
WithCaptcha trait and <x-captchaapi::livewire-form> component skipped
the kill-switch, so a disabled install still wired up the captcha rule and
the event-mode form. Both now respect the flag.setup-php step to accept the new GitHub App
token format.(2.1.3 was never released.)
config/captchaapi.php.captchaapi.enabled config key as a master kill-switch for the
package. When set to false (via CAPTCHAAPI_ENABLED=false in .env),
ValidCaptcha::validate() passes silently and
<x-captchaapi::widget /> renders nothing. Lets you keep the
validation rule, the Livewire trait, and the Blade markup in place
across environments that don't have a live site key (local dev, CI,
staging) without conditional render boilerplate or stubbed config.
Defaults to true, so existing installs are unaffected. The new
accessor Captchaapi::enabled() exposes the resolved value for
consumer code that wants to gate its own logic on the same flag.
Distinct from FakeCaptchaapi::enable(): fake mode is a per-test
bypass that throws outside the testing environment; the new
enabled flag is a permanent runtime switch safe to set in any
environment.
<x-captchaapi::error /> Blade component for rendering the
CAPTCHA validation error (expired attestation, replay attempt, bad
signature). Defaults to :for="captcha_attestation" — matching the
field name used by WithCaptcha and <x-captchaapi::livewire-form>
— and renders a <p role="alert">. Both :for and the wrapping tag
(:as) are overridable, and extra attributes (e.g. class) merge
through. Internally a thin wrapper around Laravel's [@error](https://github.com/error)
directive; the README documents the manual [@error](https://github.com/error) form too for
projects that prefer to keep markup in their own templates.Per-request memoization in ValidCaptcha so the rule is safe to
call multiple times with the same attestation within a single HTTP
request. The canonical case is Laravel Fortify: its
Fortify::authenticateUsing callback fires twice per login attempt
(once via RedirectIfTwoFactorAuthenticatable::validateCredentials,
once via AttemptToAuthenticate::handle), so a captcha rule wired
into that callback would on its second invocation hit the jti cache
set by its first invocation and reject as a replay. After this fix,
the second within-request call short-circuits via the memoization
flag and returns success without re-claiming the cache key. Replay
protection across requests still works exactly as before.
Recommended deployment: bump to 1.0.2, then re-enable
CAPTCHAAPI_REPLAY_PROTECTION=true in any project that had to
disable it as a workaround.
img.shields.io/packagist/l/... (depends on Packagist API + shields.io
cache, returned "not found" until both refreshed after registration)
to a static MIT badge. License is constant; no reason to fetch it
dynamically.WithCaptcha trait and <x-captchaapi::livewire-form> Blade
component use surface (#[Validate] attribute, wire:model,
$wire.set / $wire.{method}) that is identical between Livewire 3
and 4, so production users on Livewire 3 will likely still work.
However, the test infrastructure (Testbench × Livewire 3 ×
Laravel 11) trips on a realtime-facade autoloading timing issue
inside Livewire 3's SupportFileUploads::provide() boot path, which
cannot be reasonably worked around at the package level. CI now
tests Livewire 4 only; composer.json require-dev and suggest
drop the ^3.0 constraint.New "Status element styling" section in README documenting the three
customization paths for the widget's visual status feedback:
default colors out-of-the-box, per-state CSS override, or wholesale
opt-out via data-captcha-no-color attribute. Reflects the
underlying captcha.js change (now served from captchaapi.eu) which
removed status element auto-injection in favour of strict opt-in,
and switched default colors from inline style.color to a low-
specificity injected stylesheet so customer CSS overrides cleanly
without !important.
Package code is unchanged — this is documentation catching up to upstream behaviour customers see when they install today.
Initial release.
Captchaapi\Laravel\Rules\ValidCaptcha — local HMAC verification of
captchaapi.eu attestations. No HTTP round-trip on form submit.'captcha' validation alias for use as a Laravel string rule.CAPTCHAAPI_SECRET_KEYS env
variable for zero-downtime secret rotation.replay_protection, default on).<x-captchaapi::widget /> Blade component — renders the client-side
widget script with all configuration knobs filled in from
config/captchaapi.php.<x-captchaapi::livewire-form> Blade component — drop-in form wrapper
that uses the widget's event mode and dispatches to a Livewire action.Captchaapi\Laravel\Concerns\WithCaptcha Livewire trait — provides the
$captcha_attestation property and validation rule wiring.Captchaapi\Laravel\Facades\Captchaapi facade with fake() /
unfake() / isFake() for test mode bypass.Captchaapi\Laravel\Testing\FakeCaptchaapi test helper.en + cs shipped, more locales follow consumer demand).How can I help you explore Laravel packages today?