Skip to content

Instantly share code, notes, and snippets.

@babacarcissedia
Last active February 9, 2024 01:41
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save babacarcissedia/4128edae15b2ff070dbb828a39d77097 to your computer and use it in GitHub Desktop.
Save babacarcissedia/4128edae15b2ff070dbb828a39d77097 to your computer and use it in GitHub Desktop.
Add google recaptcha to website

Opinionated code Laravel code for adding recaptcha on website. Bot protection especially on open form (without user authentication)

  1. Create blade component for ease of reuse
resources/views/components/input/captcha.blade.php

@props([
    'id',
    'name',
    'action'
])

{{--
<x-input.captcha
    name="captcha"
    id="captcha_user"
    action="user_create"
/>
--}}

<script>
    (function () {
        const id = @json($id);
        const captchaKey = @json(config('services.google.captcha.public_key'));
        const action = @json($action);
        const onLoaded = () => {
            console.log('recaptcha on loaded')
            if (!window.grecaptcha) {
                console.error('Google catcha not loaded. Aborting...')
                return;
            }
            grecaptcha.ready(function() {
                console.log('grecaptcha ready')
                grecaptcha.execute(captchaKey, { action }).then(function(token) {
                    const captchaInput = document.querySelector('#' + id) || {}
                    captchaInput.value = token
                    console.log({token})
                });
            });
        };
        const resource = 'https://www.google.com/recaptcha/api.js?render=' + captchaKey;
        const container = document.head;
        let script = container.querySelector(`script[data-captcha=google]`);
        if (script) {
            onLoaded()
            return;
        }
        script = document.createElement('script')
        script.src = resource
        script.setAttribute('data-captcha', 'google')
        script.addEventListener('load', onLoaded)
        container.appendChild(script)
        if (document.readyState === 'complete') {
            onLoaded()
        } else {
            document.addEventListener('DOMContentLoaded', onLoaded)
        }
    })()
</script>
<input type="hidden" id="{{ $id }}" name="{{ $name }}">
  1. Add to form markup
<form action="{{ route('user.store') }}" class="form" method="post">
      @csrf
      ...

      <x-input.captcha
          name="captcha"
          id="captcha_user"
          action="user_create"
      />

</form>
  1. Add form request
// ContactRequest.php

    public function rules(): array
    {
        return [
            // ... your other fields
            'captcha_user' => [
                'required',
            ]
        ];
    }

    public function messages(): array
    {
        return [
            'captcha_user' => __('validation.captcha')
        ];
    }

    public function validateCaptcha(): bool
    {
        return app(CaptchaClient::class)->verify(
            $this->input('captcha'),
            $this->input('captcha_action')
        );
    }
  1. Add google recaptcha client
// CaptchaClient
<?php

namespace Neptune\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;

class CaptchaClient
{
    private string $secret;

    /**
     * CaptchaService constructor.
     * @param $secret
     */
    public function __construct($secret)
    {
        $this->secret = $secret;
    }

    public function verify($action, $input)
    {
        $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
            'secret' => $this->secret,
            'response' => $input,
        ])
            ->json();
        $isSuccess = $response['success'];


        if (! $response['success']) {
            throw ValidationException::withMessages([
                'captcha' => __('validation.captcha', ['reason' => $response['error-codes'][0]]),
            ]);
        }

        if ($response['action'] != $action) {
            throw ValidationException::withMessages([
                'captcha' => __('validation.captcha', ['reason' => 'action mismatch']),
            ]);
        }
        if ($response['score'] < .8) {
            throw ValidationException::withMessages([
                'captcha' => __('validation.captcha', ['reason' => 'score did not pass minimum mark']),
            ]);
        }
    }
}
  1. Add handler code in controller
// Controller

public function store (ContactRequest $request) {
        $request->validateCaptcha();
        // further logic
}

All good now your form should resist bots a bit better

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment