Implementing Google reCAPTCHA v3 Validation in Laravel 11: A Step-by-Step Guide - cygner
Laravel

Implementing Google reCAPTCHA v3 Validation in Laravel 11: A Step-by-Step Guide

Step 1: Install Laravel 11

If you already have a running Laravel project, you can skip this step. However, if you need to create a new Laravel 11 project, follow the command below to set up your application:

composer create-project laravel/laravel google-recaptcha-example-app


This command will create a new Laravel application named google-recaptcha-example-app.

Step 2: Add Google API Key

To integrate Google reCAPTCHA v3 into your Laravel 11 application, you need to configure the Site Key and Secret Key. If you don’t already have these keys, you can generate them by following the steps below:

  • Visit the Google reCAPTCHA Admin Console.
  • Log in with your Google account.
  • Register a new site.
  • Fill out the form:
    • Label: Provide a name for your project (e.g., "Laravel Recaptcha Example").
    • reCAPTCHA Type: Select reCAPTCHA v3.
    • Domains: Enter your application’s domain (e.g.,  localhost or 127.0.0.1 or google-recaptcha-example-app.com for local development or your production domain).

Accept the terms of service and click Submit.



Once the site is registered, you will receive a
Site Key and a Secret Key as like below:



Next, add these keys to your
.env file:

RECAPTCHA_SITE_KEY=your-site-key-here
RECAPTCHA_SECRET_KEY=your-secret-key-here


Ensure these keys are securely stored and not exposed in your codebase. With the API keys configured, you're now ready to proceed to the next step!

Step 3: Create a Validation Rule

In this step, we will create a custom validation rule named ReCaptcha to verify user submissions against the Google reCAPTCHA v3 API. This rule will help determine whether the request is from a real user or a bot.

Step 3.1: Generate the Validation Rule

php artisan make:rule ReCaptcha


This will generate a new file located at: app/Rules/ReCaptcha.php

Step 3.2: Update the ReCaptcha Rule

Open the ReCaptcha.php file and update it as follows:

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Http;

class ReCaptcha implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string, ?string=): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $response = Http::get("https://www.google.com/recaptcha/api/siteverify",[
            'secret' => env('RECAPTCHA_SECRET_KEY'),
            'response' => $value
        ]);

        if (!($response->json()["success"] ?? false)) {
            $fail('The google recaptcha is required.');
        }
    }
}


Step 4: Create a Controller

In this step, we will create a new controller named ContactController to handle the contact form logic, including reCAPTCHA validation.

Step 4.1: Generate the Controller
Run the following Artisan command to create the controller:

php artisan make:controller ContactController


This command will create a new file located at:
app/Http/Controllers/ContactController.php

Step 4.2: Update the Controller

Open the ContactController.php file and add the following methods:

<?php

namespace App\Http\Controllers;

use App\Rules\ReCaptcha;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class ContactController extends Controller
{
    /**
     * Display the contact form.
     */
    public function index(): View
    {
        return view('contact');
    }

    /**
     * Handle the form submission.
     */
    public function store(Request $request): RedirectResponse
    {
        // Validate the form inputs
        $request->validate([
            'name' => 'required',
            'phone' => 'required|digits:10|numeric',
            'message' => 'required',
            'g-recaptcha-response' => ['required', new ReCaptcha],
        ]);

        // Retrieve all form inputs
        $input = $request->all();

        /*--------------------------------------------
        | Write code here to store the data in the database
        | Example: Contact::create($input);
        --------------------------------------------*/

        // Redirect back with a success message
        return redirect()->back()->with(['success' => 'Contact form submitted successfully.']);
    }
}


Step 5: Create the View File

In this step, we create a Blade view file, contact.blade.php, to display a contact form integrated with Google reCAPTCHA v3.

File Location:
resources/views/contact.blade.php

Open the contact.blade.php file and add the following code:

<!DOCTYPE html>
<html>
<head>
    <title>Implementing Google reCAPTCHA v3 Validation in Laravel 11</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css" />
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://www.google.com/recaptcha/api.js"></script>
</head>
<body>
    <div class="container">
        <div class="row mt-5 mb-5">
            <div class="col-10 offset-1 mt-5">
                <div class="card">
                    <div class="card-header bg-primary">
                        <h3 class="text-white">Implementing Google reCAPTCHA v3 Validation in Laravel 11</h3>
                    </div>
                    <div class="card-body">
                        <form method="POST" action="{{ route('contact.store') }}" id="contactUSForm">
                            {{ csrf_field() }}
                              
                            <!-- Name Field -->
                            <div class="row">
                                <div class="col-md-12">
                                    <div class="form-group">
                                        <strong>Name:</strong>
                                        <input type="text" name="name" class="form-control" placeholder="Name" value="{{ old('name') }}">
                                        @if ($errors->has('name'))
                                            <span class="text-danger">{{ $errors->first('name') }}</span>
                                        @endif
                                    </div>
                                </div>
                            </div>

                            <!-- Phone Field -->
                            <div class="row">
                                <div class="col-md-12">
                                    <div class="form-group">
                                        <strong>Phone:</strong>
                                        <input type="text" name="phone" class="form-control" placeholder="Phone" value="{{ old('phone') }}">
                                        @if ($errors->has('phone'))
                                            <span class="text-danger">{{ $errors->first('phone') }}</span>
                                        @endif
                                    </div>
                                </div>
                            </div>

                            <!-- Message Field -->
                            <div class="row">
                                <div class="col-md-12">
                                    <div class="form-group">
                                        <strong>Message:</strong>
                                        <textarea name="message" rows="3" class="form-control">{{ old('message') }}</textarea>
                                        @if ($errors->has('message'))
                                            <span class="text-danger">{{ $errors->first('message') }}</span>
                                        @endif
                                        @if ($errors->has('g-recaptcha-response'))
                                            <span class="text-danger">{{ $errors->first('g-recaptcha-response') }}</span>
                                        @endif
                                    </div>  
                                </div>
                            </div>
                   
                            <!-- Submit Button with reCAPTCHA -->
                            <div class="form-group text-center">
                                <button class="g-recaptcha btn btn-success btn-submit" 
                                    data-sitekey="{{ env('RECAPTCHA_SITE_KEY') }}" 
                                    data-callback='onSubmit' 
                                    data-action='submit'>Submit</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
  
<script type="text/javascript">
    function onSubmit(token) {
        document.getElementById("contactUSForm").submit();
    }
</script>
</html>


Step 6: Create Routes

In this step, we define the routes for the contact form. One route will display the form (GET request), and the other will handle form submissions (POST request).

File Location:
routes/web.php

Add the Routes below code:

<?php

use App\Http\Controllers\ContactController;
use Illuminate\Support\Facades\Route;

// Route to display the contact form
Route::get('contact', [ContactController::class, 'index']);

// Route to handle the contact form submission
Route::post('contact', [ContactController::class, 'store'])->name('contact.store');


By adding these routes, the contact form is now fully functional. Users can access the form via /contact and submit it, where reCAPTCHA validation will ensure security.