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,
    ));
}