ReCaptcha
Integrate reCAPTCHA using async HTTP/2, making your app fast with a few lines.
use Illuminate\Support\Facades\Route;
Route::post('login', function () {
// ...
})->middleware('recaptcha:checkbox');
Keep this package free
Your support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can spread the word!
Requirements
- Laravel 9.x, or later
- PHP 8.0 or later
Installation
You can install the package via Composer:
composer require laragear/recaptcha
Set up
Add the reCAPTCHA keys for your site to the environment file of your project. You can add each of them for reCAPTCHA v2 checkbox, invisible, Android, and score.
If you don't have one, generate it in your reCAPTCHA Admin panel.
RECAPTCHA_CHECKBOX_SECRET=6t5geA1UAAAAAN...
RECAPTCHA_CHECKBOX_KEY=6t5geA1UAAAAAN...
RECAPTCHA_INVISIBLE_SECRET=6t5geA2UAAAAAN...
RECAPTCHA_INVISIBLE_KEY=6t5geA2UAAAAAN...
RECAPTCHA_ANDROID_SECRET=6t5geA3UAAAAAN...
RECAPTCHA_ANDROID_KEY=6t5geA3UAAAAAN...
RECAPTCHA_SCORE_SECRET=6t5geA4UAAAAAN...
RECAPTCHA_SCORE_KEY=6t5geA4UAAAAAN...
This allows you to check different reCAPTCHA mechanisms using the same application, in different environments.
ReCaptcha already comes with v2 keys for local development. For v3, you will need to create your own set of credentials once on production.
Usage
Usage differs based on if you're using checkbox, invisible, Android challenges, or the v3 score-driven challenge.
Checkbox, invisible and Android challenges
After you integrate reCAPTCHA into your frontend or Android app, set the ReCaptcha middleware in the POST
routes where a form with reCAPTCHA is submitted. The middleware will catch the g-recaptcha-response
input (you can change it later) and check if it's valid.
To declare the middleware just use the ReCaptcha
middleware builder:
ReCaptcha::checkbox()
for explicitly rendered checkbox challenges.ReCaptcha::invisible()
for invisible challenges.ReCaptcha::android()
for Android app challenges.
use App\Http\Controllers\ContactController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route;
Route::post('contact', [ContactController::class, 'send'])
->middleware(ReCaptcha::invisible()->forGuests('web')->remember())
If for some reason the challenge is not a success, the validation will immediately kick in and throw a ValidationException
, returning the user back to the form.
Remembering challenges
To avoid asking for challenges over and over again, you can "remember" the challenge for a given set of minutes. This can be enabled globally, but you may prefer to do it in a per-route basis.
Simple use the remember()
method. You can set the number of minutes to override the global parameter. Alternatively, rememberForever()
will remember the challenge forever.
use App\Http\Controllers\Auth\LoginController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route
Route::post('login', [LoginController::class, 'login'])
->middleware(ReCaptcha::invisible()->remember());
Route::post('message', [ChatController::class, 'login'])
->middleware(ReCaptcha::checkbox()->rememberForever());
You should use this in conjunction with the @robot
directive in your Blade templates to render a challenge when the user has not successfully done one before.
@robot <div class="g-recaptcha" data-sitekey="{{ recaptcha('invisible') }}" data-callback="onSubmit" data-size="invisible"> div> @endrobot
Good places to remember a challenge for some minutes are forms which are expected to fail, or when you have multiple forms the user may jump between.
Changing the input name
You can change the input name from g-recaptcha-response
, which is the default, to anything using input()
.
use App\Http\Controllers\Auth\LoginController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route
Route::post('login', [LoginController::class, 'login'])
->middleware(ReCaptcha::checkbox()->input('recaptcha_input'));
Score-driven challenge
The reCAPTCHA v3 middleware works differently from v2. This response is always a success, but the challenge scores between 0.0
and 1.0
. Human-like interaction will be higher, while robots will score lower. The default threshold is 0.5
, but this can be changed globally or per-route.
To start using it, simply use the ReCaptcha::score()
method to your route.
use App\Http\Controllers\CommentController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route
Route::post('comment', [CommentController::class, 'create'])
->middleware(ReCaptcha::score());
Once the challenge has been received in your controller, you will have access to two methods from the Request class or instance: isHuman()
and isRobot()
, which may return true
or false
:
use App\Models\Post;
use Illuminate\Http\Request;
public function store(Request $request, Post $post)
{
$request->validate([
'body' => 'required|string|max:255'
]);
$comment = $post->comment()->make($request->only('body'));
// Flag the comment as "moderated" if it was a written by robot.
if ($request->isRobot()) {
$comment->markAsModerated();
}
$comment->save();
return view('post.comment.show', ['comment' => $comment]);
}
You can also have access to the response from reCAPTCHA using the response()
method of the ReCaptcha
facade:
use Laragear\ReCaptcha\Facades\ReCaptcha;
$response = ReCaptcha::response();
if ($response->score > 0.2) {
return 'Try again!';
}
Be careful of calling
response()
, as it will throw an exception on controllers without challenges.
Threshold, action and input name
The middleware accepts three additional parameters using the middleware helper.
threshold()
: The value that must be above or equal to be considered human.action()
: The action name to optionally check against.input()
: The name of the reCAPTCHA input to verify.
use App\Http\Controllers\CommentController;
use Laragear\ReCaptcha\Http\Middleware\Builders\ReCaptcha;
use Illuminate\Support\Facades\Route
Route::post('comment', [CommentController::class, 'store'])
->middleware(ReCaptcha::score()->threshold(0.7)->action('post-comment')->input('my_score_input');
When checking the action name, ensure your frontend action matches with the expected in the middleware.
Bypassing on authenticated users
Sometimes you may want to bypass reCAPTCHA checks when there is an authenticated user, or automatically receive it as a "human" on score-driven challenges, specially on recurrent actions or when the user already has completed a challenge (like on logins).
To exclude authenticated user you can use forGuests()
, and specify the guards if necessary.
use App\Http\Controllers\CommentController;
use App\Http\Controllers\MessageController;
use DarkGhostHunter\Captcha\ReCaptcha;
use Illuminate\Support\Facades\Route
// Don't challenge users authenticated on the default (web) guard.
Route::post('message/send', [MessageController::class, 'send'])
->middleware(ReCaptcha::invisible()->forGuests());
// Don't challenge users authenticated on the "admin" and "moderator" guards.
Route::post('comment/store', [CommentController::class, 'store'])
->middleware(ReCaptcha::score(0.7)->action('comment.store')->forGuests('admin', 'moderator'));
Then, in your blade files, you can easily skip the challenge with the @guest
or @auth
directives.