Laravel hCaptcha Custom Validation Rule

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:

Create custom validation rule 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:

Initial validation rule class structure

<?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:

Initialize hCaptcha validation data

$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:

Make hCaptcha verification request

$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:

Check hCaptcha response

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:

Custom validation error message

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:

Complete hCaptcha validation rule class

<?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:

Import validation rule

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:

Add validation rule to form rules

$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!

Want to work together?

Professional developers choose Server Side Up to ship quality applications without surrendering control. Explore our tools and resources or work directly with us.

Join our community

We're a community of 3,000+ members help each other level up our development skills.

Platinum Sponsors

Active Discord Members

We help each other through the challenges and share our knowledge when we learn something cool.

Stars on GitHub

Our community is active and growing.

Newsletter Subscribers

We send periodic updates what we're learning and what new tools are available. No spam. No BS.

Sign up for our newsletter

Be the first to know about our latest releases and product updates.

    Privacy first. No spam. No sharing. Just updates.