0

Following 4.1 docs, with a mash-up of the two User entities How to Load Security Users and How to Implement a Simple Registration Form, attempts to log in result in the following dev log entries:

security.INFO: User has been authenticated successfully...
security.DEBUG: Stored the security token in the session...
request.INFO: Matched route "home"...
security.DEBUG: Read existing security token from the session...
doctrine.DEBUG: SELECT t0.id AS id_1,...
security.DEBUG: Token was deauthenticated after trying to refresh it

User entity (includes this SO answer)

class User implements UserInterface, \Serializable, EquatableInterface
{

    public function __construct()
    {
        $this->roles = array('ROLE_USER');
        $this->isActive = true;
    }
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(name="is_active", type="boolean")
     */
    private $isActive;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\NotBlank()
     * @Assert\Email()
     */
    private $email;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\NotBlank()
     */
    private $username;

    /**
     * @Assert\NotBlank()
     * @Assert\Length(max=4096)
     */
    private $plainPassword;

    /**
     * The below length depends on the "algorithm" you use for encoding
     * the password, but this works well with bcrypt.
     *
     * @ORM\Column(type="string", length=64)
     */
    private $password;

    /**
     * @ORM\Column(type="array")
     */
    private $roles;

    // other properties and methods

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function setUsername($username)
    {
        $this->username = $username;
    }

    public function getPlainPassword()
    {
        return $this->plainPassword;
    }

    public function setPlainPassword($password)
    {
        $this->plainPassword = $password;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function setPassword($password)
    {
        $this->password = $password;
    }

    public function getSalt()
    {
        return null;
    }

    public function eraseCredentials()
    {

    }

    public function getRoles()
    {
        return $this->roles;
    }

    public function addRole($role)
    {
        $role = strtoupper($role);
        if ($role === static::ROLE_DEFAULT) {
            return $this;
        }

        if (!in_array($role, $this->roles, true)) {
            $this->roles[] = $role;
        }

        return $this;
    }

    public function setActive($boolean)
    {
        $this->active = (bool) $boolean;

        return $this;
    }

    /** @see \Serializable::serialize() */
    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt,
        ));
    }

    /** @see \Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt
            ) = unserialize($serialized, ['allowed_classes' => false]);
    }

    public function isEqualTo(UserInterface $user)
    {
        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}

security.yaml:

security:
    encoders:
      App\Entity\User:
        algorithm: bcrypt

    providers:
        db_provider:
            entity:
                class: App\Entity\User
                property: username

    firewalls:
        main:
            provider: db_provider
            anonymous: ~
            form_login:
                login_path: login
                check_path: login
                default_target_path: home

    access_control:
        - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: ROLE_USER }

Controller:

public function login(Request $request, AuthenticationUtils $authenticationUtils)
{
    // get the login error if there is one
    $error = $authenticationUtils->getLastAuthenticationError();

    // last username entered by the user
    $lastUsername = $authenticationUtils->getLastUsername();

    return $this->render('security/login.html.twig', array(
            'last_username' => $lastUsername,
            'error' => $error,
    ));
}
geoB
  • 4,578
  • 5
  • 37
  • 70
  • Normally you would setUser::roles to a doctrine array in the constructor. Not sure if it matters in that case. Verify the user row in your database has been populated correctly. Maybe verify the sql query as well. And comment out the access_control section until things are working. And maybe add a dump statement or two to the isEqualTo method. I think that is what is probably failing though I don't see why. – Cerad Jun 23 '18 at 01:10
  • Thanks for the suggestions. User row is verified good; query is verified good; isEqualTo returns true. Behavior is independent of constructor roles array content and `is_active` value. Without access control login is redirected but user is anon. Added explicit session parameters to framework.yaml; session now created but is empty. – geoB Jun 23 '18 at 12:45
  • Well, you certainly do need a session. I have actually solved several similar questions by running the actual code. Consider checking your setup into a github repository and I'll take a look. – Cerad Jun 23 '18 at 13:06
  • You can find the repo [here](https://github.com/truckee/fourone). And extra big thanks. – geoB Jun 23 '18 at 13:27
  • Not seeing anything obvious. Did you write a registration page? I see the form but not the controller or template. – Cerad Jun 23 '18 at 13:44
  • No reg page yet. I wanted to know login worked first. Any reason to think the lack of a registration page is a source of error? – geoB Jun 23 '18 at 14:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173675/discussion-between-cerad-and-geob). – Cerad Jun 23 '18 at 14:10

1 Answers1

0

So after a long discussion it was determined that roles were not being set in the database. Attempts to set one in the constructor were doomed to failure since Doctrine does not use the constructor when hydrating from the database.

The proper solution is to correctly store the roles in the database. A bit of a hack is to do something like:

// User
getRoles() {
    return count($roles) ? $roles : ['ROLE_USER'];
}

And if you look closely, the addRoles method in the question has an error probably because it was partially cloned from the FOSUserBundle. Always a dangerous thing to do.

Cerad
  • 48,157
  • 8
  • 90
  • 92