0

I'd like to know if it's possible to check the ticking of a reCAPTCHA specifically in a Controller php. I want to do it so that I can make a condition in order to display an error message in case user did not tick the "I am not a robot" checkbox.

I tried to do something like this :

contact.html.twig file

{% trans_default_domain 'contact' %}

{% extends 'base.html.twig' %}

{% block main %}

{{ form_start(form, {'attr' : {'id' : 'contactform', 'novalidate' : 'novalidate'}} ) }}

{{ form_widget(form.name) }}
{{ form_label(form.name, 'contact.name' | trans ) }}

{{ form_widget(form.email) }}
{{ form_label(form.email, 'contact.email' | trans ) }}

{{ form_widget(form.message) }}
            
{% for message in app.flashes('captcha') %}
    <p class="text-danger">{{ 'contact.error.captcha' | trans }}</p>
{% endfor %}

{{ form_widget(form.save, {'label': 'contact.send' | trans } ) }}

{{ form_end(form) }}

<script>

    function renderReCaptcha(widget) {
        var form = widget.closest('form');
        var widgetType = widget.getAttribute('data-type');
        var widgetParameters = {
            'sitekey': '{{ gg_recaptcha_site_key }}'
        };

        if (widgetType == 'invisible') {
            widgetParameters['callback'] = function () {
                form.submit()
            };
            widgetParameters['size'] = "invisible";
        }

        var widgetId = grecaptcha.render(widget, widgetParameters);

        if (widgetType == 'invisible') {
            bindChallengeToSubmitButtons(form, widgetId);
        }
    }

    function bindChallengeToSubmitButtons(form, reCaptchaId) {
        getSubmitButtons(form).forEach(function (button) {
            button.addEventListener('click', function (e) {
                e.preventDefault();
                grecaptcha.execute(reCaptchaId);
            });
        });
    }

    function getSubmitButtons(form) {
        var buttons = form.querySelectorAll('button, input');
        var submitButtons = [];

        for (var i= 0; i < buttons.length; i++) {
            var button = buttons[i];
            if (button.getAttribute('type') == 'submit') {
                submitButtons.push(button);
            }
        }
        return submitButtons;
    }

</script>

{% endblock %}

ContactController.php file

<?php

namespace App\Controller\Front;

use App\Form\Front\ContactType;
use Symfony\Component\Mime\Email;
use App\Repository\UserRepository;
use App\Form\Front\ContactFromUserType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class ContactController extends AbstractController
{
    #[Route('/contact', name: 'contact')]
    public function contact(
        Request $request, 
        UserRepository $userRepository,
        MailerInterface $mailer): Response
    {
            $form = $this->createForm(ContactType::class);
            $form->handleRequest($request);

            if ($form->isSubmitted() && $form->isValid()) {
                $contactFormData = $form->getData();
                
                if ($contactFormData['g-recaptcha-response'] == "") {
                    $this->addFlash('captcha', 'Si tu n\'es pas un robot, coche la case.');
                } else {
                    $message = (new Email())
                    ->from($contactFormData['email'])
                    ->to('contact@mail.com')
                    ->subject('Message d\'un visiteur !')
                    ->text(
                        '"'.$contactFormData['message'].'"'.\PHP_EOL.\PHP_EOL.
                        $contactFormData['name'].' - '.$contactFormData['email'],
                        'text/plain'
                    );
                    $mailer->send($message);
                }
            }

            return $this->render('front/home/contact.html.twig', [
                'form' => $form->createView()
            ]);
    }
}

ReCaptchaValidationListener.php file

<?php

namespace App\Form\EventListener;

use ReCaptcha\ReCaptcha;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\Request;

class ReCaptchaValidationListener implements EventSubscriberInterface
{
    private $reCaptcha;

    public function __construct(ReCaptcha $reCaptcha)
    {
        $this->reCaptcha = $reCaptcha;
    }

    public static function getSubscribedEvents()
    {
        return [
            FormEvents::POST_SUBMIT => 'onPostSubmit'
        ];
    }

    public function onPostSubmit(FormEvent $event)
    {
        $request = Request::createFromGlobals();

        $result = $this->reCaptcha
            ->setExpectedHostname($request->getHost())
            ->verify($request->request->get('g-recaptcha-response'), $request->getClientIp());

        if (!$result->isSuccess()) {
            $event->getForm()->addError(new FormError('The captcha is invalid. Please try again.'));
        }
    }
}

Is there a solution to write this condition in my controller : if reCAPTCHA robot has been ticked when form is submitted ?

Emilie Tossan
  • 127
  • 1
  • 1
  • 16
  • Does this answer your question? [ReCaptcha v2 client side events](https://stackoverflow.com/questions/31687803/recaptcha-v2-client-side-events) – DarkBee Jul 31 '23 at 13:04
  • You can't do this clientside, unless you'd use ajax – DarkBee Jul 31 '23 at 13:04
  • Towards the end of the article they do this with form events, here is a deep-link to the section: https://qferrer.medium.com/securing-your-symfony-forms-with-google-recaptcha-v2-dbfc902b0c50#b33b – Arleigh Hix Aug 01 '23 at 08:29
  • It looks like `$request->request->get('g-recaptcha-response')` may be what you want to look at in your controller. – Arleigh Hix Aug 01 '23 at 08:37
  • It might be it. But is ```g-recaptcha-response``` the response after the user ticked on checkbox or is the fact the user pressed or not the "I am not a robot" checkbox ? – Emilie Tossan Aug 01 '23 at 18:44
  • The link you shared, I used it to used it to use ReCaptcha. So I just published my ReCaptchaValidationListener.php file content. – Emilie Tossan Aug 01 '23 at 18:47
  • `g-recaptcha-response` is the value returned and set by recaptcha *after* an user completed the challenge, which you need to use to validate the request – DarkBee Aug 02 '23 at 06:02
  • I would like to influence whether the user clicked the checkbox or not. First, I would like to know how can I know the potential values of ```g-recaptcha-response``` ? – Emilie Tossan Aug 02 '23 at 16:59
  • Did you check out the linked question I've posted? If you want to do it on the clientside, you'll need to resort to javascript – DarkBee Aug 04 '23 at 04:56
  • Yes, thank you DarkBee. I checked the link. My problem is that I am trying to find out how I can say in my Controller **when user validates the form without clicking the reCaptcha checkbox**. On the link, they propose a solution in JS when user has clicked on reCaptcha checkbox. Sorry if I wasn't clear. – Emilie Tossan Aug 04 '23 at 22:07
  • I'm sorry, I'm not sure if I'm following correctly, looking at your code, you are already verifying the result in the function `onPostSubmit` - `$this->reCaptcha....->verify(...)`. Am I missing something here? – DarkBee Aug 07 '23 at 05:48
  • I wish to write a condition in my Controller so that I can display the error message *You didn't tick reCaptcha.* in my html twig. I tried ```if ($contactFormData['g-recaptcha-response'] == "") {}``` but I don't have satisfying result. – Emilie Tossan Aug 14 '23 at 00:40

1 Answers1

-2

Yes, there is a solution to write this condition in your controller. You can use the g-recaptcha-response request parameter to check if the user has ticked the reCAPTCHA checkbox. If the value of this parameter is empty, then the user has not ticked the checkbox.

Here is an example of how you can do this in your controller:

public function contact(Request $request)
{
    $form = $this->createForm(ContactType::class);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $contactFormData = $form->getData();

        $gRecaptchaResponse = $request->request->get('g-recaptcha-response');

       //handle captcha verification here
       

        //if (captchaSuccess) {
             //your logic
        //} else {
            //$this->addFlash('captcha', 'The captcha is invalid. Please try again.');
        //}
    }

    return $this->render('front/home/contact.html.twig', [
        'form' => $form->createView()
    ]);
}

Explanation:

We are first checking if the form is submitted and valid. If it is, we then get the value of the g-recaptcha-response request parameter. If the value is empty, then we know that the user has not ticked the reCAPTCHA checkbox. We can then add a flash message to the session to inform the user that they need to tick the checkbox.

Edit

If the value of the g-recaptcha-response request parameter is not empty, than you have to verify the token and based on the success of verification, you can proceed further.

For captcha verification you can follow this blog or you can utilize Gregwar's CaptchaBundle

Ghulam Farid
  • 470
  • 4
  • 10
  • "*If the value of the g-recaptcha-response request parameter is not empty, then we know that the user has ticked the reCAPTCHA checkbox*" - This is incorrect, you'd still have to verify the token to determine if the request was valid or not – DarkBee Aug 07 '23 at 05:46
  • Yes, We have to append some verification process to verify Captcha – Ghulam Farid Aug 07 '23 at 07:21
  • Thank you very much for your response Ghulam. I added ```if ($contactFormData['g-recaptcha-response'] == "") {}``` but nothing happens. What did I do wrong ? – Emilie Tossan Aug 14 '23 at 00:32
  • @EmilieTossan, did you actually see the `g-recaptcha-response` key in your request? If yes what value you're receiving in this scenario with or without checking the captcha? – Ghulam Farid Aug 15 '23 at 17:12
  • How do I test it exactly ? I added ```dd($contactFormData['g-recaptcha-response']);``` when my form has been submitted and I have this error : *Undefined array key "g-recaptcha-response"*. – Emilie Tossan Aug 16 '23 at 10:03
  • It's mean you're not actually getting the captcha in your controller, and when you tried to debug, you got the error. I'm sure if you'll `dd($contactFormData['g-recaptcha-response'])`, you'll not see the `g-recaptcha-response` in the your form data. Could you please share, what're you doing on client side? – Ghulam Farid Aug 17 '23 at 14:22