Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token was deauthenticated Error Symfony 4 #9914

Closed
wants to merge 4 commits into from

Conversation

walterwhites
Copy link

@walterwhites walterwhites commented Jun 11, 2018

We should no long used Serializable interface without username/email, password, id attributes it's caused error when you log with a user 'Token was deauthenticated after trying to refresh it'
Here I propose to add EquatableInterface and isEqualTo method to choose what attributes do you want compare for each request.

We should no long used Serializable interface in Symfony 4 it's caused error when you log with a user 'Token was deauthenticated after trying to refresh it'
we should use EquatableInterface and isEqualTo method
@xabbuh
Copy link
Member

xabbuh commented Jun 12, 2018

Implementing the EquatableInterface could be considered. But we IMO should keep implementing the Serialiable interface to control what exactly gets written to the session.

@walterwhites
Copy link
Author

@xabbuh

We also have:
logout_on_user_change
type: boolean default: false
New in version 3.4: The logout_on_user_change option was introduced in Symfony 3.4.
If true this option makes Symfony to trigger a logout when the user has changed. Not doing that is deprecated, so this option should be set to true to avoid getting deprecation messages.
The user is considered to have changed when the user class implements EquatableInterface and the isEqualTo() method returns false. Also, when any of the properties required by the UserInterface (like the username, password or salt) changes.

@xabbuh
Copy link
Member

xabbuh commented Jun 13, 2018

I am not sure that I understand what you are trying to say. Can you elaborate a bit on why you think we should drop the Serializable implementation.

@walterwhites
Copy link
Author

@xabbuh

If you're curious about the importance of the serialize() method inside the User class or how the User object is serialized or deserialized, then this section is for you. If not, feel free to skip this.

Once the user is logged in, the entire User object is serialized into the session. On the next request, the User object is deserialized. Then, the value of the id property is used to re-query for a fresh User object from the database. Finally, the fresh User object is compared to the deserialized User object to make sure that they represent the same user. For example, if the username on the 2 User objects doesn't match for some reason, then the user will be logged out for security reasons.

Even though this all happens automatically, there are a few important side-effects.

First, the Serializable interface and its serialize() and unserialize() methods have been added to allow the User class to be serialized to the session. This may or may not be needed depending on your setup, but it's probably a good idea. In theory, only the id needs to be serialized, because the refreshUser() method refreshes the user on each request by using the id (as explained above). This gives us a "fresh" User object.

But Symfony also uses the username, salt, and password to verify that the User has not changed between requests (it also calls your AdvancedUserInterface methods if you implement it). Failing to serialize these may cause you to be logged out on each request. If your user implements the EquatableInterface, then instead of these properties being checked, your isEqualTo() method is called, and you can check whatever properties you want. Unless you understand this, you probably won't need to implement this interface or worry about it.

So the problem if you use Serializable interface, for that it works it should contains $id, $email or $username and $password
like that (if there missing one of them user will lohout after each request):

 public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->email,
            $this->password
        ));
    }

But in symfony 4 Serializable interface isn't really useful, because you can implements EquatableInterface and put isEqualTo method that make the job and you can pass it what do you want compare (you can put only password if you want or add attributes ...) and it compare at each request if user have changed or not, for exemple:

/**
     * The equality comparison should neither be done by referential equality
     * nor by comparing identities (i.e. getId() === getId()).
     *
     * However, you do not need to compare every attribute, but only those that
     * are relevant for assessing whether re-authentication is required.
     *
     * @return bool
     */
    public function isEqualTo(UserInterface $user)
    {
        if ($this->password !== $user->getPassword()) {
            return false;
        }


        return true;
    }

To resume we can use Serializable Interface in User class, but is not usefull in this context of a User class because we can implements EquatableInterface (and it's useless to keep the both interfaces)

@xabbuh
Copy link
Member

xabbuh commented Jun 13, 2018

Well, my point is that when not implementing Serializable, all object attributes will be part of the serialised string. IMO it's likely that this is not the desired behaviour for the common use case and why I would like to keep that part here in the docs.

@walterwhites
Copy link
Author

@xabbuh Ok but we should add Equatable interface exemple maybe ?

@xabbuh
Copy link
Member

xabbuh commented Jun 13, 2018

Yes, adding Equatable does make sense to me. But we should add a comment that explains when you need to do that.

@walterwhites
Copy link
Author

@xabbuh Ok, I added it :)

}

/**
* if you want to keep the control on what attributes are are compared at each request to know if user have changed,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double are 😉

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxhelias thanks it fix

@@ -42,12 +42,14 @@ with the following fields: ``id``, ``username``, ``password``,

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Serializable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be reverted. We follow the Symfony code style which does not add use statements for classes from the global namespace.

/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->username, // you should use $this->email if you don't use username but email to log user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should remove this comment. Similar things apply to all other attributes as well if you change their names.

return false;
}

if ($this->username !== $user->getUsername()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not look realistic to me. Users are refreshed by their username. So this should always be the same.

@javiereguiluz
Copy link
Member

As explained in #10151, we're refactoring Security docs completely. So let's close this pull request and once the refactor is finished, let's rethink about this. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants