Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Google Recaptcha V3 Laravel Package

timehunter/laravel-google-recaptcha-v3

Laravel Google reCAPTCHA v3 integration featuring a Blade directive to inject the script, server-side token validation against Google's API, configurable score thresholds, and support for multiple actions. Invisible spam and bot protection with no user interaction.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install

composer require timehunter/laravel-google-recaptcha-v3

2. Publish the config (Laravel auto-discovers the provider on 8+)

php artisan vendor:publish --tag=config

This gives you config/recaptcha.php — this is the first file to open. It holds the site_key, secret_key, and the score/option settings the package passes to Google. Everything else in the package is a thin wrapper around those values.

3. Register keys from the reCAPTCHA v3 admin console (choose v3, not v2). Put them in .env and reference them from the config:

RECAPTCHA_SITE_KEY=6Lc...
RECAPTCHA_SECRET_KEY=6Lc...

4. Render the client script once per page — put this in your base layout, before </body>:

{!! Recaptcha::script() !!}

The site key is public; the secret key must never reach the browser.

5. First real use case: protecting a login or signup form. In the browser you fetch a token for a named action, then POST it:

grecaptcha.ready(() => {
  grecaptcha.execute('{{ config('recaptcha.site_key') }}', { action: 'login' })
    .then(token => document.querySelector('#recaptcha-token').value = token);
});

On the server, verify before authenticating:

if (! Recaptcha::verify($request->input('recaptcha_token'), 'login')) {
    return back()->withErrors(['recaptcha' => 'Verification failed.']);
}

If that round-trip works once, you understand the whole package — v3 is a score endpoint plus plumbing.

Implementation Patterns

Use named actions deliberately. execute(siteKey, { action: 'signup' }) and verify($token, 'signup') must match. Google scopes the score to the action, so reuse one action name across different forms dilutes the signal and lets a token obtained on a low-value page be replayed against a sensitive one. Treat actions as a small enum you own.

Wrap verification in FormRequests. The cleanest day-to-day pattern is a RecaptchaRule or a withValidator hook so controllers stay free of verification noise:

public function rules(): array
{
    return [
        'email'    => 'required|email',
        'password' => 'required',
        'recaptcha_token' => 'required|string',
    ];
}

protected function passedValidation(): void
{
    abort_unless(Recaptcha::verify($this->recaptcha_token, 'login'), 422);
}

Apply a per-form score threshold. Don't hard-code 0.5 globally. Logins and payments usually want 0.7+ and a follow-up (email code, 2FA); content comments can tolerate 0.3 with moderation. Decide per action, not per app.

Middleware for route groups. If a whole group (e.g. /api/contact/*) needs reCAPTCHA, a small middleware that checks a header or body token keeps the check out of every controller. Keep it off for GET routes and auth-free pages — v3 tokens only make sense when a user action produces one.

Verification happens once, immediately. Because tokens are single-use and short-lived, the pattern is: user clicks → grecaptcha.execute → POST → server verifies synchronously in the same request. Do not queue the verification or store the token for a later job.

Testing. Bind a fake to the facade in your test setup so suites never hit Google:

Recaptcha::shouldReceive('verify')->andReturn(true);

For feature tests that exercise the real controller path with a token field, this is the only realistic option — you cannot mint valid tokens in CI.

Graceful degradation. If Recaptcha::verify throws (network, bad secret, timeout), decide explicitly: on public signup you may fail open with logging; on password reset you should fail closed. Put that decision in your controller or rule, not in the package call.

Gotchas and Tips

  • v3 is not a challenge. It returns a score (0.0–1.0) and never blocks by itself. If you need interactive puzzles, that's v2/Enterprise — different keys, different endpoints, incompatible.
  • Tokens expire in ~2 minutes and are single-use. A user who sits on a form fails verification. Either re-execute on submit or refresh the token shortly before POST. The most common "it works in dev, fails in prod" bug is a token fetched on page load.
  • Score ≠ truth. Google's own guidance: treat low scores as a signal to add friction (2FA, moderation), not as an automatic ban. Users on VPNs, privacy extensions, and corporate NATs routinely score low.
  • Action mismatch silently fails. If the client sends action: 'submit' and you verify with 'login', the check fails. Log both values during rollout.
  • Client IP and hostname. The package often forwards the request IP to Google. Behind Cloudflare/ELB an untrusted proxy can make that IP wrong and depress scores; confirm trustProxies is configured before blaming Google.
  • Keys are environment-specific. Test-site keys and production keys are different consoles. Also note Google returns a pass for localhost in many setups — don't infer production behavior from local scores.
  • Config caching bites. After editing config/recaptcha.php or .env in a deployed environment, run php artisan config:clear (and restart workers if using Octane). The package reads config at verify time, so stale cache equals stale keys.
  • Wrap the call, don't trust the return blindly. Check the package's documented return (bool vs response object) — some versions return false on network error and some throw. Wrap in try/catch and log the exception message; Google's failure reasons (timeout-or-duplicate, invalid-input-secret, bad-action) are actionable.
  • Don't send the secret client-side. Obvious, but it's the #1 leak with this library class — people render {{ Recaptcha::script() }} and are tempted to inline the config array.
  • Extension point: the config options array. Anything Google's siteverify accepts (e.g. remoteip) can usually be passed there without touching the package — try that before forking.
  • Small package, few dependents. Pin the version and read the changelog on upgrades; there's no large community to surface regressions for you, so cover your verification path with one integration test.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels