reCAPTCHA + Laravel + Vue
Tags: captcha, vue, laravel, inertiajs
Published: 2021-12-13 19:10:22

---
title: reCAPTCHA + Laravel + Vue
published: true
description: We'll use a reCAPTCHA Vue package in the frontend. Then use a reCAPTCHA PHP package to configure the backend via a form submission route middleware.
tags: captcha, Vue, Laravel, InertiaJS
//cover_image: https://direct_url_to_image.jpg
---

We'll implement a reCAPTCHA Vue package in the frontend and a PHP package for the backend. We'll ensure each step is working as expected along the way.

## Table of contents
 * [Prerequisites](#prerequisites)
 * [1. Provide reCAPTCHA keys](#1-provide-recaptcha-keys)
 * [2. Prepare the front-end](#2-prepare-the-front-end)
 * [3. Error Handling](#3-error-handling)

## Prerequisites <a name="prerequisites"></a>

Assumes you have a working Laravel 8 and Vue 2.0 project, Composer and NPM, and a Vue form to which you want to add reCAPTCHA.

This was built onto a project that uses InertiaJS. However, I believe this _should_ work on a separated front-end and back-end. As such, each sub-section is prefixed with either **(Laravel)** or **(Vue)** to indicate to which side the changes belong.

## 1. Provide reCAPTCHA keys <a name="1-provide-recaptcha-keys"></a>

We'll need a valid SITEKEY and SECRET from Google.

### (Laravel) Store environment variables

Add keys to your `.env` file.

> NOTE: Do not commit your `.env` file. You should commit a `.env.example` with secure values redacted.

```
RECAPTCHAV2_SITEKEY=<yoursitekeyhere>
RECAPTCHAV2_SECRET=<yoursecrethere>
```

> **WARNING:** Do not expose `RECAPTCHAV2_SECRET` to the front-end.
>
> _If using VueCLI_, environment variables are embedded into the build, meaning anyone can view them by inspecting your app's files. _If using VueCLI_, **Do not** store this value in _any_ `.env` file on Production, not even `.env.production`. They are auto-loaded by VueCLI. It is okay to expose the `SITEKEY`, but for the SECRET, use some other secure method in Production, such as setting environment variables without this `dotenv` file.

Define these environment variables in Laravel's configuration.

Create a `config/recaptchav2.php` file:

```php
<?php
return [
    'origin' => env('RECAPTCHAV2_ORIGIN', 'https://www.google.com/recaptcha'),
    'sitekey' => env('RECAPTCHAV2_SITEKEY', ''),
    'secret' => env('RECAPTCHAV2_SECRET', '')
];
```

### (Laravel) Share environment variable with front-end

Here's that InertiaJS piece mentioned in the prerequisites above. 

The gist of this change is that we want to share the `RECAPTCHAV2_SITEKEY` environment variable with the front-end. 

If not using InertiaJS, you should be able to provide the environment variable to Vue some other way (such as mentioned above if using VueCLI). 

Here's an InertiaJS way of doing it:

We do this with InertiaJS middleware in `app/Http/Middleware/HandleInertiaRequests.php`.

```patch
 public function share(Request $request)
 {
     return array_merge(parent::share($request), [
+        // Provide the reCAPTCHA site key to the front-end.
+        'recaptchav2_sitekey' => config('recaptchav2.sitekey'),
+        // DO NOT SHARE RECAPTCHAV2_SECRET.
     ]);
 }
```

#### (Vue) Ensure successful share to front-end

In your Vue form somewhere, we'll temporarily plop in the following to ensure the key is being passed successfully from the backend.

```patch
 <template>
+  {{ $page.props.recaptchav2_sitekey }}
   ...
 </template>
```

> This may be different depending on the route taken to expose the environment variable. With VueCLI it would be `{{ process.env.RECAPTCHAV2_SITEKEY }}`.

Browsing the page should display your site key.

## 2. Prepare the front-end <a name="2-prepare-the-front-end"></a>

We have a securely stored secret key, and can display the site key on the page. Now, let's get a working reCAPTCHA to appear on the page. 

### (Vue) Add reCAPTCHA Vue

Require the reCAPTCHA Vue package.

```
npm install --save @vue/composition-api vue-recaptcha
```

Add the reCAPTCHA to your component.

```patch
 <template>
-  {{ $page.props.recaptchav2_sitekey }}
   <form>
     ...
+    <vue-recaptcha 
+      :sitekey="$page.props.recaptchav2_sitekey" 
+    />
   </form>
 </template>

+<script>
+import { VueRecaptcha } from 'vue-recaptcha';
+
+export default {
+  components: {
+    VueRecaptcha,
+  },
```

At this point, you should have a visible, working reCAPTCHA on your page.

## 3. Prepare the back-end

### (Laravel) Add middleware

```
composer require google/recaptcha
```

Define a reCAPTCHA middleware class.

```php
<?php
/**
 * @file app/Http/Middleware/Recaptcha.php
 */

namespace App\Http\Middleware;

use Closure;
use ReCaptcha\ReCaptcha as GoogleRecaptcha;

class Recaptcha
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = (new GoogleRecaptcha(config('recaptchav2.secret')))
            ->verify($request->input('g-recaptcha-response'), $request->ip());

        if (!$response->isSuccess()) {
            return redirect()->back()->with('status', 'Recaptcha failed. Please try again.');
        }

        return $next($request);
    }
}
```

List this new middleware in `/app/Http/Kernel.php`:

```patch
 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
+'recaptcha' => \App\Http\Middleware\Recaptcha::class,
 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
```

Then, attach this middleware to your form's submit route.

```patch
 Route::post('/my-form-action', [
     MyFormController::class, 
     'submitForm'
-]);
+])
+    ->middleware('recaptcha');
```

### (Vue) Pass the reCAPTCHA response

The reCAPTCHA backend expects a token response from the front-end.

```patch
-    <vue-recaptcha 
-      :sitekey="$page.props.recaptchav2_sitekey" 
+      @verify="onCaptchaVerify"
-    />
 ...
 methods: {
+  onCaptchaVerify(token) {
+    // Provide the token response to the form object.
+    this.form['g-recaptcha-response'] = token;
   },
```

> Note: This assumes you are using a form data object.

At this point, you can verify you are human and submit the form. If you fail to check the CAPTCHA box, submitting the form should redirect you back with a `Recaptcha failed. Please try again.` status.

## 3. Error Handling <a name="3-error-handling"></a>

On submit, if the reCAPTCHA has failed or has not been attempted, we want to indicate that to the visitor.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/avk945u0ja4mtm02rq56.png)

Make the following additions:

```patch
 <vue-recaptcha
   :sitekey="$page.props.recaptchav2_sitekey"
   @verify="onCaptchaVerify"
 />
+<span
+  v-if="recaptcha_error"
+  class="error-message"
+>
+  Please confirm you are not a robot.
+</span>
 ...
   data() {
     return {
+      recaptcha_error: false,
       form: {
         ...
       }
     }
   },
   methods: {
     onSubmit() {
+      if (!this.form['g-recaptcha-response']) {
+        // Show error after submit.
+        this.recaptcha_error = true;
+      } else {
+        // Proceed with submission.
+      }
     }
```

Enjoy your functioning reCAPTCHA.