Integrate reCAPTCHA using async HTTP/2, making your app fast with a few lines.

Overview

ReCaptcha

Latest Version on Packagist Latest stable test run Codecov coverage Maintainability Sonarcloud Status Laravel Octane Compatibility

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.

  1. threshold(): The value that must be above or equal to be considered human.
  2. action(): The action name to optionally check against.
  3. 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.

<form id="comment" method="post">
    <textarea name="body"></textarea>

    @auth
        <button type="submit">Post comment</button>
    @else
        <button class="g-recaptcha" data-sitekey="{{ captchavel('invisible') }}" data-callback="onSubmit">
            Post comment
        </button>
    @endauth
</form>

Faking reCAPTCHA scores

You can easily fake a reCAPTCHA response score in your local development by setting RECAPTCHA_FAKE to true.

RECAPTCHA_FAKE=true

This environment variable allows to fake a robot responses by filling the is_robot input in your form.

<form id="comment" method="post">
    <textarea name="body"></textarea>

    @env('local')
        <input type="checkbox" name="is_robot" checked>
    @endenv

    <button class="g-recaptcha" data-sitekey="{{ captchavel('invisible') }}" data-callback='onSubmit'>
        Post comment
    </button>
</form>

Confirmation middleware

ReCaptcha comes with a handy middleware to confirm with a simple reCAPTCHA Checkbox challenge, much like the password confirmation middleware included in Laravel.

First, set the route to protect using the recaptcha.confirm middleware.

use Illuminate\Support\Facades\Route;

Route::get('/settings', function () {
    // ...
})->middleware('recaptcha.confirm');

Route::post('/settings', function () {
    // ...
})->middleware('recaptcha.confirm');

Once done, ensure you have also a recaptcha.confirm route to receive the redirected user, and one to receive the challenge at the same path (as the view POST request does). ReCaptcha comes with a controller and a basic view that you can use out of the box:

use Illuminate\Support\Facades\Route;
use Laragear\ReCaptcha\Http\Controllers\ConfirmationController;

Route::get('recaptcha', [ConfirmationController::class, 'show'])
    ->name('recaptcha.confirm');

Route::post('recaptcha', [ConfirmationController::class, 'confirm'])

When the user tries to enter the route, it will be redirected to the view asking to resolve a reCAPTCHA challenge. Once done, it will be redirected to the intended URL.

img.png

The middleware it's compatible with remembering challenges, and will use the default amount of time to not ask again if remembering only when it's enabled globally, otherwise it will be asked to confirm every time.

You can configure the route name and the guards to bypass the confirmation if the user is authenticated after the first argument.

use Illuminate\Support\Facades\Route;

Route::get('/settings', function () {
    // ...
})->middleware('recaptcha.confirm:my-custom-route,web,admin');

Frontend integration

Check the official reCAPTCHA documentation to integrate the reCAPTCHA script in your frontend, or inside your Android application.

You can use the recaptcha() helper to output the site key depending on the challenge version you want to render: checkbox, invisible, android or score (v3).

<form id='login' method="POST">
    <input type="email" name="email">
    <input type="password" name="password">

    <button class="g-recaptcha" data-sitekey="{{ recaptcha('invisible') }}" data-callback='onSubmit'>
        Login
    </button>
</form>

Advanced configuration

ReCaptcha is intended to work out-of-the-box, but you can publish the configuration file for fine-tuning the reCAPTCHA verification.

php artisan vendor:publish --provider="Laragear\ReCaptcha\ReCaptchaServiceProvider" --tag="config"

You will get a config file with this array:

<?php

return [
    'enable'            => env('RECAPTCHA_ENABLE', false),
    'fake'              => env('RECAPTCHA_FAKE', false),
    'hostname'          => env('RECAPTCHA_HOSTNAME'),
    'apk_package_name'  => env('RECAPTCHA_APK_PACKAGE_NAME'),
    'threshold'         => 0.5,
    'remember' => [
        'enabled' => false,
        'key'     => '_recaptcha',
        'minutes' => 10,
    ],
    'client' => [
        'version' => 2.0,
    ],
    'credentials'       => [
        // ...
    ]
];

Enable Switch

return [
    'enable' => env('RECAPTCHA_ENABLE', false),
];

By default, ReCaptcha is disabled, so it doesn't check reCAPTCHA challenges, and on score-driven routes it will always resolve them as human interaction.

You can enable it with the RECAPTCHA_ENABLE environment variable.

RECAPTCHA_ENABLE=true

This can be handy to enable on some local or development environments to check real interaction using the included localhost test keys, which only work on localhost.

When switched off, the reCAPTCHA v2 challenges are not validated in the Request input, so you can safely disregard any frontend script or reCAPTCHA tokens or boxes.

Fake responses

RECAPTCHA_FAKE=true

If ReCaptcha is enabled, setting this to true will allow your application to fake v3-score responses from reCAPTCHA servers. For v2 challenges, setting this to true bypasses the challenge verification.

You should enable it for running unit tests.

Warning - Remember to disable faking on production. Not doing so will fake all score challenges as human, not requiring the challenge token.

Hostname and APK Package Name

RECAPTCHA_HOSTNAME=myapp.com
RECAPTCHA_APK_PACKAGE_NAME=my.package.name

If you are not verifying the Hostname or APK Package Name in your reCAPTCHA Admin Panel, may be because you use multiple hostnames or apps, you will have to issue them in the environment file.

When the reCAPTCHA response from the servers is retrieved, it will be checked against these values when present. In case of mismatch, a validation exception will be thrown.

Threshold

return [
    'threshold' => 0.4
];

The default threshold to check against reCAPTCHA v3 challenges. Values equal or above will be considered "human".

If you're not using reCAPTCHA v3, or you're fine with the default, leave this alone. You can still override the default in a per-route basis.

Remember

return [
    'remember' => [
        'enabled' => false,
        'key'     => '_recaptcha',
        'minutes' => 10,
    ],
];

Remembering the user once a V2 challenge is successful is disabled by default.

It's recommended to use a per-route basis "remember" if you expect only some routes to remember challenges, instead of the whole application.

This also control how many minutes to set the "remember". You can set INF constant to remember the challenge forever (or until the session expires).

HTTP Client options

return [
    'client' => [
        'version' => 2.0,
    ],
];

This array sets the options for the outgoing request to reCAPTCHA servers. This is handled by Guzzle, which in turn will pass it to the underlying transport. Depending on your system, it will probably be cURL.

By default, it instructs Guzzle to use HTTP/2 whenever possible.

Credentials

return [
    'credentials' => [
        // ...
    ]
];

Here is the full array of reCAPTCHA credentials to use depending on the version. Do not change the array unless you know what you're doing.

Developing with ReCaptcha v2

On local development, let the default testing keys be used. These are meant to be used on local development, so in production you can easily change them for real keys.

On unit testing, the middleware will detect the environment and skip the mandatory challenge check. There is no need to disable ReCaptcha.

Developing with ReCaptcha v3 (score)

On local development and unit testing, the middleware and will automatically create human responses. There is no need to disable ReCaptcha, but enabling faking is mandatory to enable faking robot responses using is_robot on live requests.

Inside your tests, you can fake a response made by a human or robot by simply using the fakeHuman() and fakeRobot() methods, which will score 1.0 or 0.0 respectively for all subsequent requests.

<?php

use Laragear\ReCaptcha\Facades\ReCaptcha;

// Let the user login normally.
ReCaptcha::fakeHuman();

$this->post('login', [
    'email' => '[email protected]',
    'password' => '123456',
])->assertRedirect('user.welcome');

// ... but if it's a robot, force him to use 2FA.
ReCaptcha::fakeRobot();

$this->post('login', [
    'email' => '[email protected]',
    'password' => '123456',
])->assertViewIs('login.2fa');

Fake responses don't come with actions, hostnames or APK package names, so these are not validated.

Alternatively, fakeScore() method will fake responses with any score you set.

<?php

use Laragear\ReCaptcha\Facades\ReCaptcha;

// A human comment should be public.
ReCaptcha::fakeScore(0.8);

$this->post('comment', [
    'body' => 'This comment was made by a human',
])->assertSee('Your comment has been posted!');

// Moderate a post if there is no clear separation.
ReCaptcha::fakeScore(0.4);

$this->post('comment', [
    'body' => 'Comment made by something.',
])->assertSee('Your comment will be reviewed before publishing.');

// A robot shouldn't be able to comment.
ReCaptcha::fakeScore(0.2);

$this->post('comment', [
    'body' => 'Comment made by robot.',
])->assertSee('Robots are not welcomed here! Go away!');

PhpStorm stubs

For users of PhpStorm, there is a stub file and a meta file to aid in macro autocompletion for this package. You can publish them using the phpstorm tag:

php artisan vendor:publish --provider="Laragear\ReCaptcha\ReCaptchaServiceProvider" --tag="phpstorm"

The file gets published into the .stubs folder of your project, while the meta is located inside .phpstorm.meta.php directory. You should point your PhpStorm to these stubs.

Laravel Octane compatibility

  • The only singleton registered is the ReCaptcha class, which uses a stale config instance. You shouldn't change the config.
  • There are no static properties written during a request.

There should be no problems using this package with Laravel Octane as intended.

HTTP/3

Currently, HTTP/3 is on RFC Proposed Standard. Until it's Internet Standard, cURL and Guzzle and reCAPTCHA servers must implement the finished state of the protocol, which as of today is still a moving target.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

License

This specific package version is licensed under the terms of the MIT License, at time of publishing.

Laravel is a Trademark of Taylor Otwell. Copyright © 2011-2022 Laravel LLC.

You might also like...
Stupid async implementation using await-generator

libAsync Stupid async implementation using await-generator Usage libAsync::doAsync(Closure $executor); // -- Returns a promise Example Fetch data fro

Talkino allows you to integrate multi social messengers and contact into your website and enable your users to contact you using multi social messengers' accounts.

Talkino Welcome to our GitHub Repository Talkino is a click to chat plugin to show your agents’ multiple social messengers, phone and emails on the ch

Venture allows you to create and manage complex, async workflows in your Laravel apps.

Venture is a package to help you build and manage complex workflows of interdependent jobs using Laravel's queueing system. Installation Note: Venture

PHP Machine Learning Rain Forecaster is a simple machine learning experiment in predicting rain based on a few forecast indicators.

PHP Machine Learning Rain Forecaster is a simple machine learning experiment in predicting rain based on a few forecast indicators.: forecasted "HighT

Ip2region is a offline IP location library with accuracy rate of 99.9% and 0.0x millseconds searching performance. DB file is ONLY a few megabytes with all IP address stored. binding for Java,PHP,C,Python,Nodejs,Golang,C#,lua. Binary,B-tree,Memory searching algorithm

Ip2region是什么? ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nodejs,golang,c#等查询绑定和Binary,B树,内存三种查询算法。 Ip2region特性

Yclas Self Hosted is a powerful script that can transform any domain into a fully customizable classifieds site within a few seconds.

Yclas 4.4.0. Description Yclas self-hosted is a powerful script that can transform any domain into a fully customizable classifieds site within a few

A first party module to integrate Elastic App Search in Magento 2.
A first party module to integrate Elastic App Search in Magento 2.

A first-party Magento integration for building excellent, relevant search experiences with Elastic App Search. ⚠️ This is a beta version of the client

Use php-fpm as a simple built-in async queue

PHP-FPM Async Queue Use php-fpm as a simple built-in async queue. Based on interoperable queue interfaces Queue Interop. Usage composer makasim/php-fp

An async process dispatcher for Amp.

process This package provides an asynchronous process dispatcher that works on all major platforms (including Windows). As Windows pipes are file hand

Releases(v1.0.2)
Owner
Laragear
Packages for Laravel
Laragear
Making multiple identical function calls has the same effect as making a single function call.

Making multiple identical function calls has the same effect as making a single function call.

李铭昕 4 Oct 16, 2021
Integrate your PHP application with your HTTP caching proxy

FOSHttpCache Introduction This library integrates your PHP applications with HTTP caching proxies such as Varnish. Use this library to send invalidati

FriendsOfSymfony 338 Dec 8, 2022
Fly50W is a new language which helps you build simple apps using more than 500k lines of code easily.

Fly50W is a new language which helps you build simple apps using more than 500k lines of code easily. Installation

null 4 Jun 22, 2022
Here are few exercises to practice how to implement API Security with NGINX App-Protect WAF.

api-security-lab This repo contains files for customers and partners to practice an API Security with NGINX App-Protect WAF. To demonstrate the capabi

null 4 Mar 30, 2022
A web app for the resolution of a mobile game in wich you have 4 images and a list of letters, then a few boxes to fill with the word connecting the four images.

4images_1mot_solutions A web app for the resolution of a mobile game in wich you have 4 images and a list of letters, then a few boxes to fill with th

FOTSO Claude 3 Jan 13, 2022
:passport_control: Helper for Google's new noCAPTCHA (reCAPTCHA v2 & v3)

noCAPTCHA (new reCAPTCHA) By ARCANEDEV© What is reCAPTCHA? reCAPTCHA is a free service that protects your site from spam and abuse. It uses advanced r

ARCANEDEV 342 Dec 30, 2022
🔒 Built a recaptcha for Nifty that works with Wordpress's Gravity Forms

Recaptcha Fully Functioning spam filter that has 10 levels of security, but is slim and rpackaged to integrate with any Gravity Forms form WORKING EXA

Lisa Broadhead 1 May 17, 2022
A few Fat-Free specific extensions for Tracy Debugger to help debug your code quickly.

Fat-Free Tracy Extensions This is a set of extensions to make working with Fat-Free a little richer. F3 - Analyze all hive variables. Database - Analy

Austin 6 Nov 17, 2022
Matplotlib. Pyplot written in PHP. Graphs charts, scatter, plot, lines, custom graphs

php-libplot. Written in PHP. Graphs charts, scatter, plot, lines, custom graphs Matplotlib. Pyplot written in PHP. Graphs charts, scatter, plot, lines

Rafael Martin Soto 4 Dec 30, 2022
Preload your sweet sweet code to opcache with a composer command, making your code faster to run.

Composer Preload Preload your sweet sweet code to opcache with a composer command, making your code run faster. Composer Preload is a composer plugin

Ayesh Karunaratne 197 Dec 6, 2022