Skip to content

Instantly share code, notes, and snippets.

@karser
Forked from nuryagdym/LoginSubcriber.php
Created July 23, 2020 10:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karser/8f9cd4aed270af4f30ac7b0d8513cadf to your computer and use it in GitHub Desktop.
Save karser/8f9cd4aed270af4f30ac7b0d8513cadf to your computer and use it in GitHub Desktop.
Symfony 4.4 google captcha integration to login form using KarserRecaptcha3Bundle
<?php
namespace App\EventSubscriber;
use App\Form\LoginType;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Security;
class LoginSubscriber implements EventSubscriberInterface
{
/**
* @var FormFactoryInterface
*/
private $formFactory;
/**
* @var FlashBagInterface
*/
private $flashBag;
public function __construct(FlashBagInterface $flashBag, FormFactoryInterface $formFactory)
{
$this->formFactory = $formFactory;
$this->flashBag = $flashBag;
}
/**
* @return array
*/
public static function getSubscribedEvents()
{
/**
* You can add event subscriber on KernelEvents::REQUEST with priority 9.
* because class Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener(responsible for registering the events for symfony firewall) has priority 8.
*/
return array(
KernelEvents::REQUEST => ['onLogin', 9]
);
}
/**
* @param RequestEvent $event
*/
public function onLogin(RequestEvent $event)
{
if ('public_login' !== $event->getRequest()->attributes->get('_route')) {
return;
}
//form generation should be in the same way (createdNamed in this case) as in LoginController
$loginForm = $this->formFactory->createNamed(null, LoginType::class);
if (!$loginForm->has('captcha')) {
return;
}
$loginForm->handleRequest($event->getRequest());
if (!$loginForm->isSubmitted()) {
return;
}
if (!$loginForm->get('captcha')->isValid()) {
$errors = $loginForm->get('captcha')->getErrors();
$message = count($errors) ? $errors[0]->getMessage() : 'Failed to pass robot test';
$this->flashBag->add(
'error',
$message
);
$session = $event->getRequest()->getSession();
$session->set(Security::LAST_USERNAME, $loginForm->get('_username')->getData());
//to prevent request to call next event
$event->setResponse(new RedirectResponse($event->getRequest()->getRequestUri()));
}
}
}
<?php
namespace App\Form;
use Karser\Recaptcha3Bundle\Form\Recaptcha3Type;
use Karser\Recaptcha3Bundle\Validator\Constraints\Recaptcha3;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class LoginType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('_username', EmailType::class, ['attr' => ['placeholder' => 'email'], 'empty_data' => $options['lastUsername']])
->add('_password', PasswordType::class, ['attr' => ['placeholder' => 'password']])
->add('_remember_me', CheckboxType::class, ['required' => false])
;
$builder->add('captcha', Recaptcha3Type::class, [
'constraints' => new Recaptcha3(),
'action_name' => 'login'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
//default csrf parameters defined in Symfony codes. without this configuratio csrf check will fail
'csrf_field_name' => '_csrf_token',
'csrf_token_id' => 'authenticate',
'lastUsername' => null
]);
}
}
@bencagri
Copy link

rocks 🚀

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