Easily integrate Google reCAPTCHA v3 into your Laravel application.
This is totally compatible with reCAPTCHA v2, so you can use both. Check this GitHub comment about the caveats.
You can install the package via composer:
composer require darkghosthunter/captchavel
The first thing you need is to add the RECAPTCHA_V3_KEY
and RECAPTCHA_V3_SECRET
environment variables in your .env
file with your reCAPTCHA Site Key and Secret Key, respectively. If you don't have them, you should go to your Google reCAPTCHA Admin console and create them for your application.
RECAPTCHA_V3_KEY=JmXJEeOqjHkr9LXEzgjuKsAhV84RH--DvRJo5mXl
RECAPTCHA_V3_SECRET=JmXJEeOqjHkr9WjDR4rjuON1MGxqCxdOA4zDTH0w
Captchavel by default works on auto
mode, allowing you minimal configuration in the backend and frontend. Let's start with the latter.
Just add the data-recaptcha="true"
attribute to the forms where you want to have the reCAPTCHA check. A JavaScript will be injected in all your responses that will detect these forms an add a reCAPTCHA token to them so they can be checked in the backend.
<form action="/login" method="post" data-recaptcha="true">
@csrf
<input type="text" class="form-control" name="username" placeholder="Username">
<input type="password" class="form-control" name="password" placeholder="Password">
<button type="submit" class="btn btn-primary">Log in</button>
</form>
The Google reCAPTCHA script from Google will be automatically injected on all responses for better analytics.
Check the
manual
mode if you want control on how to deal with the frontend reCAPTCHA script.
After that, you should add the recaptcha
middleware inside your controllers that will receive input and you want to protect with the reCAPTCHA check.
You can use the isHuman()
and isRobot()
methods in the Request instance to check if the request was made by a human or a robot, respectively.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class CustomController extends Controller
{
/**
* Create a new CustomController instance
*
* @return void
*/
public function __construct()
{
$this->middleware('recaptcha')->only('form');
}
/**
* Receive the HTTP POST Request
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function form(Request $request)
{
$request->validate([
'username' => 'required|string|exists:users,username'
]);
if ($request->isRobot()) {
return response()->view('web.user.pending_approval');
}
return response()->view('web.user.success');
}
}
Since it's a middleware, you can alternatively set it inside your route declaration:
<?php
use Illuminate\Support\Facades\Route;
Route::post('form')->uses('CustomController@form')->middleware('recaptcha');
The
recaptcha
middleware only works onPOST/PUT/PATCH/DELETE
methods, so don't worry if you use it in aGET
method. You will receive a niceInvalidMethodException
so you can use it correctly.
You can access the reCAPTCHA response in four ways:
- using dependency injection,
- using the
ReCaptcha
facade anywhere in your code, - the
recaptcha()
helper, - and resolving it from the Service Container with
app('recaptha')
.
These methods will return the reCAPTCHA Response from the servers, with useful helpers so you don't have to dig in the raw response:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use DarkGhostHunter\Captchavel\ReCaptcha;
class CustomController extends Controller
{
/**
* Create a new CustomController instance
*
* @return void
*/
public function __construct()
{
$this->middleware('recaptcha')->only('form');
}
/**
* Receive the HTTP POST Request
*
* @param \Illuminate\Http\Request $request
* @param \DarkGhostHunter\Captchavel\ReCaptcha $reCaptcha
* @return \Illuminate\Http\Response
* @throws \DarkGhostHunter\Captchavel\Exceptions\RecaptchaNotResolvedException
*/
public function form(Request $request, ReCaptcha $reCaptcha)
{
$request->validate([
'username' => 'required|string|exists:users,username'
]);
if ($reCaptcha->isRobot()) {
return response()->view('web.user.you-are-a-robot');
}
return response()->view('web.user.success');
}
// ...
}
The class has handy methods you can use to check the status of the reCAPTCHA information:
isResolved()
: Returns if the reCAPTCHA check was made in the current RequestisHuman()
: Detects if the Request has been made by a Human (equal or above threshold).isRobot()
: Detects if the Request has been made by a Robot (below threshold).since()
: Returns the time the reCAPTCHA challenge was resolved as a Carbon timestamp.
If you try to check if the response while the reCAPTCHA wasn't resolved, you will get a
RecaptchaNotResolvedException
.
When developing, this package registers a transparent middleware that allows you to work on your application without contacting reCAPTCHA servers ever. Instead, it will always generate a successful "dummy" response with a 1.0
score.
You can override the score to an absolute 0.0
in two ways:
- appending the
is_robot
key to the Request query,
POST http://myapp.com/login?is_robot
- or adding a checkbox with the name
is_robot
checked.
<form action="http://myapp.com/login" method="post" data-recaptcha="true">
<!-- ... -->
<input id="is_robot" type="checkbox" name="is_robot" checked>
<label for="is_robot">Filled by a robot</label>
<button type="submit">Login</button>
</form>
If you want to connect to the reCAPTCHA servers on local
environment, you can set the CAPTCHAVEL_LOCAL=true
in your .env
file.
The transparent middleware also registers itself on testing environment, so you can test your application using requests made by a robot and made by a human just adding an empty
_recaptcha
input. Sweet!
For finner configuration, publish the configuration file for Captchavel:
php artisan vendor:publish --provider="DarkGhostHunter\Captchavel\CaptchavelServiceProvider"
You will get a config file with this array:
<?php
return [
'mode' => env('CAPTCHAVEL_MODE', 'auto'),
'enable' => env('CAPTCHAVEL_ENABLE', true),
'key' => env('RECAPTCHA_V3_KEY'),
'secret' => env('RECAPTCHA_V3_SECRET'),
'threshold' => 0.5,
'request_method' => null,
];
Captchavel works painlessly once installed. You can modify the behaviour with just changing the CAPTCHAVEL_MODE
to auto
or manual
, since the config file just picks up the environment file values.
CAPTCHAVEL_MODE=auto
The auto
option leverages the frontend work from you. Just add the data-recaptcha="true"
attribute to the forms where you want to check for reCAPTCHA.
<form action="/login" method="post" data-recaptcha="true">
@csrf
<input type="text" class="form-control" name="username" placeholder="Username">
<input type="password" class="form-control" name="password" placeholder="Password">
<button type="submit" class="btn btn-primary">Log in</button>
</form>
Captchavel will inject the Google reCAPTCHA v3 as a deferred script before <head>
tag, in every response (except JSON, AJAX or anything non-HTML), so it can have more analytics about how users interact with your site.
To override the script that gets injected, take a look in the editing the script view section.
This will disable the global middleware that injects the Google reCAPTCHA script in your frontend. You should check out the Google reCAPTCHA documentation on how to implement it yourself.
Since the frontend won't have nothing injected, this mode it gives you freedom to:
- manually include the
recaptcha-inject
middleware only in the routes you want, - or include the
recaptcha::script
blade template in your layouts you want.
The manual mode is very handy if your responses have a lot of data and want better performance, because the middleware won't look into the responses.
By default, this package is transparent on local
and testing
environments, so you can develop without requiring to use reCAPTCHA anywhere.
For troubleshooting, you can forcefully enable Captchavel setting enable_local
to true
, or better, using your environment .env
file and setting CAPTCHAVEL_LOCAL
to true
.
<?php
return [
'enable_local' => env('CAPTCHAVEL_LOCAL', false),
];
These parameters are self-explanatory. One is the reCAPTCHA Site Key, which is shown publicly in your views, and the Secret, which is used to recover the user interaction information privately inside your application.
If you don't have them, use the Google reCAPTCHA Admin console to create a pair.
Google reCAPTCHA v3 returns a score for interactions. Lower scores means the Request has been probably made by a robot, while high scores mean a more human-like interaction.
By default, this package uses a score of 0.5, which is considered sane in most of cases, but you can override it using theCAPTCHAVEL_THRESHOLD
key with float values between 0.1 and 1.0.
CAPTCHAVEL_THRESHOLD=0.7
Aside from that, you can also override the score using a parameter within the recaptha
middleware, which will take precedence over the default score (set or not). For example, you can set it lower for comments, but higher for product reviews.
<?php
use Illuminate\Support\Facades\Route;
Route::post('{product}/comments')
->uses('Product/CommentController@create')
->middleware('recaptcha:0.3');
Route::post('{product}/review')
->uses('Product/ReviewController@create')
->middleware('recaptcha:0.8');
The Google reCAPTCHA library underneath allows to make the request to the reCAPTCHA servers using a custom "Request Method". The request_method
key accepts the Class you want to instance.
The default null
value is enough for any normal application, but you're free to, for example, create your own logic or use the classes included in the ReCaptcha package (that this package requires).
<?php
return [
// ...
'request_method' => 'App\Http\ReCaptcha\GuzzleRequestMethod',
];
You can mimic this next example were we will use Guzzle.
First, we will create our GuzzleRequestMethod
with the submit()
method as required. This method will return the reCAPTCHA response from the external server using the Guzzle Client.
app\Http\ReCaptcha\GuzzleRequestMethod.php
<?php
namespace App\Http\ReCaptcha;
use ReCaptcha\RequestMethod;
use ReCaptcha\RequestParameters;
class GuzzleRequestMethod implements RequestMethod
{
// ...
/**
* Submit the request with the specified parameters.
*
* @param RequestParameters $params Request parameters
* @return string Body of the reCAPTCHA response
*/
public function submit(RequestParameters $params)
{
return (new \GuzzleHttp\Client)->post($params->toQueryString())
->getBody()
->getContents();
}
}
Then, we will add the class to the request_method
key in our configuration:
config/captchavel.php
<?php
return [
// ...
'request_method' => 'App\Http\ReCaptcha\GuzzleRequestMethod',
];
Finally, we will tell the Service Container to give our GuzzleRequestMethod
to the underneath ReCaptcha
class when Captchavel tries to instance it, using the Service Container Contextual Binding.
app\Providers\AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Http\ReCaptcha\GuzzleRequestMethod;
use ReCaptcha\ReCaptcha;
use ReCaptcha\RequestMethod;
class AppServiceProvider extends ServiceProvider
{
// ...
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// ...
// Tell the Service Container to pass our custom Request Method to the ReCaptcha client
$this->app->when(ReCaptcha::class)
->needs(RequestMethod::class)
->give(function () {
return new GuzzleRequestMethod;
});
}
}
We're leaving the Contextual Binding to you, since your requester may need some logic that a simple app()->make(MyRequester::class)
may not be sufficient.
You can edit the script Blade view under by just creating a Blade template in resources/vendor/captchavel/script.blade.php
.
This blade views requires the Google reCAPTCHA v3 script, and detects the forms that need a reCAPTCHA check to be injected inside the request to the application. The view receives the $key
variable witch is just the reCAPTCHA v3 Site Key.
There you can edit how the script is downloaded from Google, and how it checks for forms to link with the backend.
The MIT License (MIT). Please see License File for more information.