Laravel hCaptcha Custom Validation Rule

Dan Pastori avatar
Dan Pastori July 6th, 2020

If you are looking for an alternative to Google’s Captcha form validation, check out hCaptcha. It’s a simple, privacy focused alternative that provides an excellent screening system for bots trying to fill out your form.

Integrating with with Laravel is a breeze. Especially with Laravel’s Custom Validations that you can create, you can implement this validation on any form! Let’s get started!

0. Sign Up for an hCaptcha Account

Before we even get started, you will need to sign up for an hCaptcha account. Doing this will give you the public and secret keys needed to ensure that you can start validating hCaptchas.

1. Frontend Setup

For this tutorial, we are assuming you have hCaptcha set up on the front end, utilizing the javascript library of your choice. If you are using any of the big 3 frameworks (VueJS, React, Angular), hCaptcha’s official repository provides components that you can easily implement.

Once you have that set up, we can move to the next step.

2. Create a Custom Laravel Validation Rule

Laravel seems to have a solution for everything, and this one that I’ve been utilizing lately. It’s the ability to create a custom form validation rule. If you check out the documentation you will see there are already a variety of useful rules you can use when validating one of your forms. However, there will be a time where you will need to make your own (I’ll be sharing a few more of these custom rules later on).

All you need to do is run the following artisan command:

php artisan make:rule ValidHCaptcha

This will create a new custom validation rule class in the App\Rules directory of your app. You can of course move this to where ever you want your rules to be stored, just make sure you update the namespace correctly.

3. Breaking Down the Custom Rule

Let’s take a look at the file that was just created:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class ValidHCaptcha implements Rule
{
    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        //
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

The first method is the passes() method which accepts attribute and value as the parameters. The value is what we are most interested in because that’s what we will be validating. This method is the heart of what we will be implementing.

The second method, message() is the customizable error string that gets sent back to the user when the validation fails. Super handy for proper UX and telling the user exactly what went wrong when their form doesn’t validate correctly.

Okay, time to implement the hCaptcha validation.

4. Implement hCaptcha Validation Logic

So to implement the hCaptcha validation logic, the best source of documentation in plain PHP can be found in this article on their official blog. We will be modifying their PHP request to work with our custom validation rule.

In the passes() method on your rule, start by adding the following code:

$data = array(
     'secret' => env('H_CAPTCHA_SECRET'),
     'response' => $value
);

These are the values you will be sending over to the hCaptcha server to validate that it was accurate. Now remember, the user validates on the front end and it generates a secure response. This response is sent along through a variable on your form named h-captcha-response (more on this later). Combined with your secret token from your hCaptcha account, this will verify that the user passed the test and it came from your server.

When I got my secret, I stored it in my .env file within Laravel to make it easy to reference later on. In this case, in the ValidHCaptcha rule. I named the variable H_CAPTCHA_SECRET.

After your data array, add the following code:

$verify = curl_init();
    
curl_setopt($verify, CURLOPT_URL, "https://hcaptcha.com/siteverify");
curl_setopt($verify, CURLOPT_POST, true);
curl_setopt($verify, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($verify, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($verify);
$responseData = json_decode( $response );

This is simply the request to the verification URL for hCaptcha. We send along the $data array we just created and will receive a response from the server. When we have that response we can determine if passed or not using the following code:

if($responseData->success) {
    return true;
} else {
    return false;
}

With any custom validation rule in Laravel, you simply return a boolean (true or false) on whether it passed or not. No matter how complex, the final result is true or false. In our case, the server will return a success message that we get when we json_decode the response. If the success is true, then we return true. If the success is false, then we return false.

That’s it for the validation logic! Now we have a re-usable validator we can use with our other form validations and on any form that requires an hCaptcha.

5. Implement the Failed Message

Now all we need to do in our rule is add the message for what gets returned when the field fails to validate (in our case, when we get an invalid response from the hCaptcha) server.

To do this, we implement the message() method on the ValidHCaptcha rule. This method simply returns a string that will get added to the error array in your form validator:

public function message()
{
    return 'Invalid CAPTCHA. You need to prove you are human.';
}

That’s all for implementing our rule! Our last step will be adding it to our validation array for our form.

Our final custom validation rule class should look like:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class ValidHCaptcha implements Rule
{
    public function __construct()
    {
        //
    }

    public function passes( $attribute, $value )
    {
        $data = array(
            'secret' => env('H_CAPTCHA_SECRET'),
            'response' => $value
        );
            
        $verify = curl_init();
            
        curl_setopt($verify, CURLOPT_URL, "https://hcaptcha.com/siteverify");
        curl_setopt($verify, CURLOPT_POST, true);
        curl_setopt($verify, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($verify, CURLOPT_RETURNTRANSFER, true);
        
        $response = curl_exec($verify);
        $responseData = json_decode( $response );

        if($responseData->success) {
            return true;
        } else {
            return false;
        }
    }

    public function message()
    {
        return 'Invalid CAPTCHA. You need to prove you are human.';
    }
}

6. Using our New Rule

There are multiple ways you can do form validation in Laravel whether it’s through a request validator, validation method on your controller or service, etc. They all operate the same way so the implementation will be the same.

Wherever you are validating your form, you need to include our new rule where you add your use statements:

use App\Rules\ValidHCaptcha;

Now we have this rule at our disposal! Next, we need to go to our rules array for the form we are validating and add the following:

$rules = [
    'h-captcha-response' => ['required', new ValidHCaptcha()]
];

So what we are adding is a validation for a field named h-captcha-response. Any form that has an hCaptcha should have this field as required first of all so we can validate it.

Next, once it’s been filtered through the required validation, we have our new ValidHCaptcha() validation. This is the rule we just created! In the passes() method it will receive the value of the h-captcha-response from the submitted form and run it through our validator which returns true or false.

One quick thing to note about custom validators is you have to include all validations for that field as an array! Sometimes you can use a pipe (|) syntax to achieve the proper validation. Since we are using a class that we created, we must add that as an array to the field we are validating. You can simply split up your previous pipe syntax into an array by removing the pipes and putting each validation as an array value.

That’s it! We now have a successful hCaptcha validation on our form on the Laravel side. Feel free to reach out if you have any questions in the comment section below. Custom form validations are extremely helpful when building APIs. If you want to learn more about building APIs with Laravel and VueJS, check out our book. There will be a variety of different examples, along with explaining some of the gotchas when using these validations with an API. We will also cover how to display the messages back through VueJS to the user!

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.