<?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;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* This event subscriber validates the catpcha's response using the google Recaptcha PHP
* library.
*
* @see https://medium.com/@qferrer/securing-your-symfony-forms-with-google-recaptcha-v2-dbfc902b0c50
*
* @author jra
*
*/
class ReCaptchaValidationListener implements EventSubscriberInterface
{
/**
* @var \ReCaptcha\ReCaptcha
*/
private $reCaptcha;
/**
* @var \Symfony\Contracts\Translation\TranslatorInterface
*/
protected $translator;
/**
* @var string
*/
private $invalidMessage = 'error.form.captcha_invalid';
public function __construct(ReCaptcha $reCaptcha, TranslatorInterface $translator)
{
$this->reCaptcha = $reCaptcha;
$this->translator = $translator;
}
public static function getSubscribedEvents()
{
return [
FormEvents::POST_SUBMIT => 'onPostSubmit'
];
}
public function setInvalidMessage($message)
{
$this->invalidMessage = $message;
return $this;
}
public function onPostSubmit(FormEvent $event)
{
$request = Request::createFromGlobals();
// This will trust all proxies
// @see : https://symfony.com/doc/current/deployment/proxies.html
$request->setTrustedProxies(['127.0.0.1', 'REMOTE_ADDR'], Request::HEADER_X_FORWARDED_ALL);
// Note that DNS resolution on the PHP server must work properly, otherwise reCaptcha will return a 'connection-refused' error
$result = $this->reCaptcha
->setExpectedHostname($request->getHost())
->verify($request->request->get('g-recaptcha-response'), $request->getClientIp());
if (!$result->isSuccess()) {
$errors = $result->getErrorCodes();
$msg = $this->translator->trans($this->invalidMessage, ['message' => implode(', ', $errors)]);
$event->getForm()->addError(new FormError($msg));
}
}
}