Add a constraint on the Symfony login form

I’ve encountered a new issue on my Symfony application and I cannot find anything to help me.

In my database, a user has an activation_token field.

When the user creates his account on my website, it generates a random token that will be used to validate his account. It will send him an email with the token in order to verify/activate his account and when he will click on it, it will set the activation_token field to null.

Creating the account works. Sending the email works. Validating the account works. I only have 1 issue.

I’d like to add a constraint that when the activation_token field is not null, the user won’t be able to log in. In short, the user will need to validate his account at first in order to be able to log in. But I have no clue how to add a constraint like that.

Currently, I can log in even if I didn’t validate my account and that’s exactly what I want to prevent.

I haven’t created a custom login form in PHP, I’m using the Security bundle from Symfony. Here we have a screenshot of my security.yaml file.

I don’t know if it’s possible to add a constraint with the Security bundle or not.

Answer

Expanding on Cerad’s comment to the question:

If you are using Symfony >= 4.4 you can write a custom UserChecker. From the documentation:

<?php

namespace AppSecurity;

use AppEntityUser as AppUser;
use AppExceptionAccountDeletedException;
use SymfonyComponentSecurityCoreExceptionAccountExpiredException;
use SymfonyComponentSecurityCoreExceptionCustomUserMessageAccountStatusException;
use SymfonyComponentSecurityCoreUserUserCheckerInterface;
use SymfonyComponentSecurityCoreUserUserInterface;

class UserChecker implements UserCheckerInterface
{
    public function checkPreAuth(UserInterface $user)
    {
        if (!$user instanceof AppUser) {
            return;
        }
        
        // this is your custom check:
        if (null !== $user->getActivationToken()) {
            throw new CustomUserMessageAccountStatusException('Please validate your account first, before logging in.');
        }
    }

    public function checkPostAuth(UserInterface $user)
    {
        // nothing to check here.
        return;
    }
}

Add the user checker to your security configuration like this:

# config/packages/security.yaml

# ...
security:
    firewalls:
        main:
            pattern: ^/
            user_checker: AppSecurityUserChecker