Laravel 7 - Generate custom captcha code

Captcha is the security to stop brute force attack. Currently there are many websites provide free Captcha services for free. Google Recaptcha is the most secured and popular service. Almost all website use their services.

Still there many developers that do not want to use third party services and want to use their own security.

In this article, we will go through on how to build own Captcha for security in your Laravel application from scratch step-by-step. I will choose mewebstudio/captcha Laravel package as it has simple and easy integration. I will create project on my local system for easyness.

Let's start! Just follow this steps and you have application ready with Captcha code security.

Step 1: Create Project

First we need to install Laravel application in our system. Make sure you have already installed LAMP stack and Composer in your system. Run the bellow command in the Terminal to create new Laravel application.

composer create-project laravel/laravel captcha

Step 2: Install captcha package

Now in the Terminal, go to the project root directory and run the bellow command to install the package.

composer require mews/captcha

Step 3: Configure the Package

Open config/app.php file and add the bellow lines in providers array.

'providers' => [
    // ...
    'Mews\Captcha\CaptchaServiceProvider',
]

In the same file add bellow lines in the aliases array.

'aliases' => [
    // ...
    'Captcha' => 'Mews\Captcha\Facades\Captcha',
]

To change in the default package setting, publish the vendor file with bellow command.

php artisan vendor:publish

This will publish all vendor files. The package will also generate config/captcha.php file. Open file and change in the captcha code as you want to change.

return [
    'default'   => [
        'length'    => 5,
        'width'     => 120,
        'height'    => 36,
        'quality'   => 90,
        'math'      => true, //Enable Math Captcha
    ],
    // ...
];

Step 4: Create Routes

Create two routes in routes/web.php for displaying form and submitting the form. Also create one route to refresh the captcha.

Route::get('contact-us', 'ContactController@contactUs')->name('contactUs');
Route::post('contact-us/submit', 'ContactController@contactUsSubmit')->name('contactUs.submit');
Route::get('refresh-captcha', 'ContactController@refreshCaptcha')->name('refreshCaptcha');

Step 5: Create Controller and controller methods

We have created the routes for contact form. Now create a controller to handle the routes.

php artisan make:controller ContactController

Also add the class methods to handle the routes.

<?php

namespace App\Http\Controllers;

use Captcha;
use Illuminate\Http\Request;

class ContactController extends Controller
{
    // ================================================
    /* method : contactUs
    * @param  : 
    * @Description : create contact us form page
    */// ==============================================
    public function contactUs()
    {
        return view('contactUs');
    }

    // ================================================
    /* method : contactUsSubmit
    * @param  : 
    * @Description : submit the contact us form
    */// ==============================================
    public function contactUsSubmit(Request $request)
    {
        $this->validate($request, [
            'name' => 'required',
            'email' => 'required|email',
            'message' => 'required|min:5',
            'captcha' => 'required|captcha', // this will validate captcha
        ]);

        // save the form in database

        return redirect()->back()->with('success', 'Your message has been received.');
    }

    // ================================================
    /* method : refreshCaptcha
    * @param  : 
    * @Description : return captcha code
    */// ==============================================
    public function refreshCaptcha()
    {
        return response()->json([
            'captcha' => Captcha::img()
        ]);
    }
}

captcha() method will return html code of captcha image. This method will help user to refresh the captcha if needed.

Step 6: Create contact form view.

At the last now we need to create contact form. Visitors of your site may want to contact you for suggestion or assistance. Create a blade file resources/views/contactUs.blade.php of contact form. I have used Laravel's default theme to save design time.

Note: Please add public/css/app.css and public/js/app.js file from the old Laravel application in this project if it is not included.

Add the required input fields in the form. At last add the Captcha::img() method that will load captcha image. Also put the ajax script to refresh the captcha image.

The form will be submit to contactUsSubmit() method and will check captcha field also.

Here is the full html code of resources/views/contactUs.blade.php file.

@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Contact us</div>
                <div class="card-body">
                    <form method="POST" action="{{ route('contactUs.submit') }}">
                        @csrf
                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="message" class="col-md-4 col-form-label text-md-right">Message</label>
                            <div class="col-md-6">
                                <textarea class="form-control @error('message') is-invalid @enderror" name="message" required></textarea>
                                @error('message')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="captcha" class="col-md-4 col-form-label text-md-right">Captcha</label>
                            <div class="col-md-6">
                                <span class="captcha-image">{!! Captcha::img() !!}</span> &nbsp;&nbsp;
                                <button type="button" class="btn btn-success refresh-button">Refresh</button>
                                <input id="captcha" type="text" class="form-control @error('captcha') is-invalid @enderror" name="captcha" required>
                                @error('captcha')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">Submit</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript">
    $(document).ready(function() {
        $('.refresh-button').click(function() {
            $.ajax({
                type: 'get',
                url: '{{ route('refreshCaptcha') }}',
                success:function(data) {
                    $('.captcha-image').html(data.captcha);
                }
            });
        });
    });
</script>
@endsection

Also create one layout file for design. I have used laravel auth design for this. Follow this article to create Laravel auth.

Now you are ready to use the captcha code. Run the Laravel start command php artisan serve in your local system and open the bellow URL in web browser.

http://localhost:8000/contact-us

This will open bellow contact form. You can also refresh the captcha with button.