Why use Google Recaptcha with Laravel Login?
Google Recaptcha is a great tool to use against spam in your applications. Whether this is a contact form, online checkout or even a login form, you’re going to want to protect yourself with Recaptcha. Laravel doesn’t come with reCaptcha out of the box. However, if you’re installing a Laravel Starter Pack, chances are you’re going to have people logging in.
In this tutorial, I will explain how to setup reCaptcha from within the Google Admin Panel to verifying recaptcha server-side in your login controller.
Google reCaptcha Admin Panel Setup
Firstly, you’re going to want to head over to Googles Recaptcha Admin Panel which can be found here; https://www.google.com/recaptcha/admin/. You may need to login to see the below control panel. To add a new site (or sites), as you can have more than one URL per reCaptcha Key, though, it is not recommended, you will need to click on the highlighted “+” icon in the screenshot below.
This will present you with the form you need to complete to setup a new set of keys for your reCaptcha installation. The form itself is pretty straight forward. You will need to add a “Label” which is what this Key/Pair will be known as. It’s usually good to make it reference the domain you are securing with reCaptcha.
For this example, we are going to be using “Score based (V3)”. This is to reduce user friction. Score based allows the user to login without the need to complete a puzzle. Finally, you are going to want to enter the domain(s) you are wanting to secure. Once this has been completed, submit the form and you will be provided with your reCaptcha Keys. Keep these keys safe as we are going to need them in the Laravel integration part next.
Laravel Integration
This section of the guide will show you how to store your keys, how to call use them and how to validate the token provided by reCaptcha. With this token, you can determine whether you want to log the user in or not.
Setting up environment variables
First, you are going to want to create a new config file in the config
directory. You can call this recaptcha.php
so we know what it references. It will look something like the following code snippet
<?php return [
/* |--------------------
| Recaptcha Site Key
|--------------------
*/
'site_key' => env('RECAPTCHA_SITE_KEY', null),
/* |--------------------
| Recaptcha Secret Key
|--------------------
*/
'secret_key' => env('RECAPTCHA_SECRET_KEY', null) ];
Here you can see we have set up two (2) new environment variables to be used in our .env
file. These are; RECAPTCHA_SITE_KEY
which is a public key and RECAPTCHA_SECRET_KEY
which is your private key.
Open up your .env
file, and scroll to the bottom of the file and add these variables. It will look something like the below snippet but with your keys not starred out.
RECAPTCHA_SITE_KEY=6Lf8***************************wNKRqXfZ
RECAPTCHA_SECRET_KEY=6Lf8D*************************JADLKtGcfVe
Now that we have added new config, we are going to need to tell Laravel they exist. The easy way to do this is to run php artisan config:cache
. This will now let us use these variables in our application.
Updating the Login Blade File
You will need to open the file resources/views/auth/login.blade.php
as we are going to be adding some code to it to enable reCaptcha on the client side. The first step we are going to do is to add a hidden field to the login form. This will be used to store the reCaptcha token on form submission to pass to the server side.
For simplicity, I usually add this just before the form
close tag. Below is the line you will need to add.
<form method="POST" action="{{ route('login') }}" id="login_form">
// Rest of Login Form
<input type="hidden" name="recaptcha" id="recaptcha" value="">
</form>
Now we have the hidden field, we need a way to generate a reCaptcha Token, and populate it into the hidden field. First, we need to include the Google reCaptcha JS file and include our public site key. You can see the config helper being used here which will pull the actual string from the environment variables we set earlier.
The final change we need to make to login.blade.php
. This is some JavaScript to listen to the form being submitted by button press, to generate and populate the token we check Server Side. The script is as follows
<script>
const login_form = document.getElementById ('login_form'); // Form
const login_submit = document.getElementById ('login_submit'); // Submit Button
login_submit.addEventListener ('click', recaptcha); function recaptcha ( e ) {
e.preventDefault ();
grecaptcha.ready (function () {
grecaptcha.execute ('{{ config('recaptcha.site_key') }}', { action: 'submit_login' })
.then (function ( token ) {
document.getElementById ('recaptcha').value = token;
login_form.submit ();
});
});
}
</script>
This will reach out to Google when the submit button is pressed to login. Go and get a reCaptcha token, populate this token in the hidden field we created previously and submit the login form for us. Now we need to make some server side changes.
Updating login form request
Out of the box, Laravel has a LoginRequest file, but it only authenticates for email and password. We need to update this to validate for the existence of a reCaptcha token. Head to the following file app\Http\Requests\Auth\LoginRequest.php
and look for the rules()
function. You are going to want to update it to include the reCaptcha token like so;
<?php
/**
* Get the validation rules that apply to the request.
*
* @return array<string, Rule|array|string>
*/
public function rules(): array
{
return [
'email' => [ 'required', 'string', 'email' ],
'password' => [ 'required', 'string' ],
'recaptcha' => [ 'required', 'string' ]
];
}
Because reCaptcha tokens can vary in length, Google recommends not checking for minimum or maximum lengths.
Updating the controller
Now that we are validating that the recaptcha
string is present, we need to head on over to app\Http\Controllers\Auth\AuthenticatedSessionController.php
to make some change. The changes we make here are to reach out to Google to validate the reCaptcha token and receive a response from Google. From there, we can handle the login.
In the controller you are going to want to look for the store()
function. We are going to use a POST request to send the data to Google before we authenticate a session in our application. The easiest way is to append your secret_key
and the token
to the authentication URL like so.
public function store( LoginRequest $request ): RedirectResponse
{
$url = 'https://www.google.com/recaptcha/api/siteverify?secret=';
$url .= config('recaptcha.secret_key');
$url .= '&response=' . $request->recaptcha;
$response = Http::post($url);
The response to the POST request should look like the below snippet.
array:5 [▼
"success" => true
"challenge_ts" => "2024-01-28T18:27:34Z"
"hostname" => "licenexv3.test"
"score" => 0.9
"action" => "submit_login"
]
The two main items we are wanting to validate against here is the success
and score
keys. This is because they tell us everything we need to know about the login request. The full store()
method with this in mind will look like the below snippet.
/** * Handle an incoming authentication request. */
public function store( LoginRequest $request ): RedirectResponse
{
$url = 'https://www.google.com/recaptcha/api/siteverify?secret=';
$url .= config('recaptcha.secret_key'); $url .= '&response=' . $request->recaptcha;
$response = Http::post($url);
if ($response->json()['success'] === false) {
return back()
->withErrors([ 'recaptcha' => 'Recaptcha verification failed, please try again.' ]);
}
if ($response->json()['score'] < 0.7) {
return back()->withErrors([ 'recaptcha' => 'Recaptcha verification failed, please try again.' ]);
}
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);
}
What are we checking.. and why?
Great question. Here we are sending the request to Google so they can analyze the token and give us information back about the user. The success
key tells us whether or not the check was successful. Meaning, we got a valid response back from Google. This does not mean the user is good to log in!
To ensure we are good to let the user login, we want to pay specific attention to the score
key that is returned. This can be anywhere between 0 and 1. The closer to 1 it is, the more genuine the request likely is. The lower the score, the more likely it is to not being a genuine request.
What score should I use to not prevent genuine logins?
This is another good question. If you have not used Google reCaptcha (v3) before or it’s the first time using it on your website, Google recommends using a score of 0.5 until you have data analytics of requests being made. You can check your reCaptcha Analytics over at Google using the following URL: https://www.google.com/recaptcha/admin/ and selecting your domain.
As your data is gathered, you may wish to increase the threshold, like in the example to a score around 0.7.
Closing Words
I hope this guide helps you secure your login portals or other forms, contact forms, or other places users can publicly post forms on your website. If you have any questions, you can reach me over on X or leave a comment below.
For more guides and hot tips like this one, please give me a follow on X/Twitter
Thank you
Originally published at https://dbirkin.dev on January 28, 2024.