Search code examples
symfony4recaptchasylius

Sylius Security: Login-form Google reCaptcha


I'm trying to add ReCaptcha to Sylius Login Form, I have installed stefandoorn/sylius-recaptcha-plugin and followed all installation instructions, but it's not validating, the form is logged even when the captcha field is empty, no error is throwed. I'm currently using Sylius v1.7

The view is working well and look like that: Login with captcha I have extended SecurityLoginType and created an Extension:

<?php

declare(strict_types=1);

namespace App\Form\Extension;

use EWZ\Bundle\RecaptchaBundle\Form\Type\EWZRecaptchaType;
use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrue as RecaptchaTrue;
use Sylius\Bundle\UiBundle\Form\Type\SecurityLoginType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;

class SecurityLoginTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('_recaptcha', EWZRecaptchaType::class, [
            'mapped' => false,
            'constraints' => [
                new RecaptchaTrue(),
            ],
            'attr' => [
                'defer' => true,
                'async' => true,
            ],
        ]);
    }

    public static function getExtendedTypes(): iterable
    {
        return [SecurityLoginType::class];
    }
}

And register the extension on config/services.yml

app.form.extension.type.security_login:
        class: App\Form\Extension\SecurityLoginTypeExtension
        tags:
            - { name: form.type_extension, extended_type: Sylius\Bundle\UiBundle\Form\Type\SecurityLoginType }

And override the login tempate in SyliusUiBundle/Security/_login.html.twig

...
{% form_theme form '@EWZRecaptcha/Form/ewz_recaptcha_widget.html.twig' %}

{{ form_errors(form._recaptcha) }}
{{ form_widget(form._recaptcha, { 'attr': {
    'options' : {
        'theme': 'light',
        'type': 'image',
        'size': 'normal'
    },
} }) }}
...

Any suggestions about it. Thanks in advance.


Solution

  • Finally I found a way to do that, as I'm using FOS bundle, symfony internally does not validate the login form itself and then we need to catch the login form before it's processed. I have added an event 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.

    Here is the code, it's explained by itself:

    <?php
    
    declare(strict_types=1);
    
    namespace App\EventSubscriber;
    
    use Sylius\Bundle\UiBundle\Form\Type\SecurityLoginType;
    use Symfony\Component\HttpKernel\Event\RequestEvent;
    
    class LoginSubscriber implements EventSubscriberInterface
    {
    
        public static function getSubscribedEvents()
        {
            return [
                KernelEvents::REQUEST => ['onLogin', 9]
            ];
        }
    
        /**
         * @param RequestEvent $event
         */
        public function onLogin(RequestEvent $event)
        {
            // Check for login route name
            if ('sylius_shop_login_check' !== $event->getRequest()->attributes->get('_route')) {
                return;
            }
    
            // Get the login form
            $loginForm = $this->formFactory->createNamed(null, SecurityLoginType::class);
    
            $loginForm->handleRequest($event->getRequest());
    
            if ($loginForm->isSubmitted() && !$loginForm->isValid()) {
                // And here code when the form is invalid
            }
        }
    }
    

    I would like to say thanks to the community. This post was very useful for me: Add listener before login