Skip to content

Instantly share code, notes, and snippets.

@bhaidar
Forked from babacarcissedia/google-recaptcha.md
Created December 15, 2023 20:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bhaidar/f56b0f286455665167e33e422d8aa7a9 to your computer and use it in GitHub Desktop.
Save bhaidar/f56b0f286455665167e33e422d8aa7a9 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 with 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="g-recaptcha"
    action="contact"
/>
--}}

<script>
    (function () {
        const id = @json($id);
        const captchaKey = @json(config('services.google.captcha.site_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('page.contact.post') }}" class="form" method="post">
      @csrf
      ...

      <x-input.captcha
          name="captcha"
          id="g-recaptcha"
          action="contact"
      />

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

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

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

    public function validateCaptcha()
    {
        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 will resist bots better

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