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
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
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 theSITEKEY
, but for the SECRET, use some other secure method in Production, such as setting environment variables without thisdotenv
file.
Define these environment variables in Laravel's configuration.
Create a config/recaptchav2.php
file:
<?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
.
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.
<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
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.
<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
/**
* @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
:
'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.
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.
- <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
On submit, if the reCAPTCHA has failed or has not been attempted, we want to indicate that to the visitor.
Make the following additions:
<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.
Top comments (0)