Search code examples
symfonysymfony-forms

Is it possible to apply validation constraints to a Symfony login form?


Is it possible to apply validation constraints to a Symfony login form?

To me it looks like that won't work. I use Symfony 5.2

I have created a Symfony login form as described on the documentation page "https://symfony.com/doc/current/security/form_login_setup.html".

Now I would like to validate the form and have created the following constraints for this in "validation.yaml".

App \ Entity \ User:
  properties:
    username:
      - NotBlank:
          message: 'form.user.username.not_blank'
    password:
      - NotBlank:
          message: 'form.user.password.not_blank'

Unfortunately the constraints are ignored.

If I leave the username and password fields blank, I get a message that the login failed. In such a case, I would like to receive the constraint message that the username and password cannot be empty.

I didn't get any further during my research on the Internet.

Could it be that no validation constraints can be used in a Symfony login form?

Has anyone of you successfully set up validation constraints in a Symfony 5 login form and can you give me a tip on what to look out for?


Solution

  • I stumbled upon a similar issue - and used the following solution:

    Since the authentification happens before the regular form validation I implemented a custom validation in the 'authenticate' method of my LoginFormAuthenticator:

    public function authenticate(Request $request): PassportInterface
    {
        $credentials = $request->get('login');
    
        <snip>
    
        $errors = $this->validateCredentials($credentials);
        if (0 !== $errors->count()) {
    
            throw new AuthenticationException();
        }
    
        return new Passport(
            new UserBadge($credentials['email']),
            new PasswordCredentials($credentials['password']),
            [
                new CsrfTokenBadge('login_token', $credentials['_csrf_token']),
                new RememberMeBadge(),
            ]
        );
    }
    

    The validateCredentials method which stores the $error-object in the session:

        public function validateCredentials($credentials) {
    
        $constraints = new Assert\Collection([
            'fields' => [
                'email' =>
                    new Assert\Sequentially([
                        new Assert\NotBlank([
                            'message' => 'login.email.not_blank'
                        ]),
                        new Assert\Email([
                            'message' => 'login.email'
                        ])
                    ]),
    
                <snip>
    
            ],
            'allowExtraFields' => true
        ]);
        $errors = $this->validator->validate(
            $credentials,
            $constraints
        );
    
        if (0 !== $errors->count()) {
            $this->session->set('login-errors', $errors);
        } else {
            $this->session->remove('login-errors');
        }
    
        return $errors;
    }
    

    The SecurityController fetches the $error-object from the session and adds the respective errors to the login form:

        $loginForm = $this->createForm(LoginType::class, $formData);
        $loginErrors = $request->getSession()->get('login-errors');
    
        if ($loginErrors) {
            foreach ($loginErrors as $error) {
                $propertyPath = trim($error->getPropertyPath(), '[]');
                $errorMessage = $error->getMessage();
                $loginForm->get($propertyPath)->addError(new FormError($errorMessage));
            }
        }
    

    Most likely not the best approach - but it does the job reasonably well and it's only the login form that makes this extra validation step necessary.