From 2a1a34936336b360cbd0336a8fe4f90c1e49aaf4 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 17 Dec 2013 21:01:54 +0100 Subject: [PATCH 1/6] Changed sha1 into bcrypt --- book/security.rst | 26 ++++++++++++++++---------- cookbook/security/entity_provider.rst | 16 +++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/book/security.rst b/book/security.rst index 2e7409bdb8a..66388dbdb1d 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1363,6 +1363,15 @@ any extra encoding. You can now calculate the hashed password either programmati Supported algorithms for this method depend on your PHP version. A full list is available calling the PHP function :phpfunction:`hash_algos`. +.. caution:: + + The above example is not meaned for practical usage, it uses a weak hash + algorithm and it is only done to be able to generate the password easily. Using + :ref:`BCrypt ` is a better option. + +.. versionadded:: 2.2 + The BCrypt encoder was introduced in Symfony 2.2. + If you're creating your users dynamically (and storing them in a database), you can use even tougher hashing algorithms and then rely on an actual password encoder object to help you encode passwords. For example, suppose your User @@ -1378,7 +1387,7 @@ configure the encoder for that user: # ... encoders: - Acme\UserBundle\Entity\User: sha512 + Acme\UserBundle\Entity\User: bcrypt .. code-block:: xml @@ -1386,7 +1395,7 @@ configure the encoder for that user: - + .. code-block:: php @@ -1395,20 +1404,17 @@ configure the encoder for that user: $container->loadFromExtension('security', array( // ... 'encoders' => array( - 'Acme\UserBundle\Entity\User' => 'sha512', + 'Acme\UserBundle\Entity\User' => 'bcrypt', ), )); -In this case, you're using the stronger ``sha512`` algorithm. Also, since -you've simply specified the algorithm (``sha512``) as a string, the system -will default to hashing your password 5000 times in a row and then encoding -it as base64. In other words, the password has been greatly obfuscated so -that the hashed password can't be decoded (i.e. you can't determine the password -from the hashed password). +In this case, you're using the strong ``bcrypt`` algorithm. This means that the +password has been greatly obfuscated so that the hashed password can't be +decoded (i.e. you can't determine the password from the hashed password). .. versionadded:: 2.2 As of Symfony 2.2 you can also use the :ref:`PBKDF2 ` - and :ref:`BCrypt ` password encoders. + password encoder. Determining the Hashed Password ............................... diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index b412c878c9f..9a7836a5314 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -257,9 +257,7 @@ then be checked against your User entity records in the database: security: encoders: Acme\UserBundle\Entity\User: - algorithm: sha1 - encode_as_base64: false - iterations: 1 + algorithm: bcrypt role_hierarchy: ROLE_ADMIN: ROLE_USER @@ -282,9 +280,7 @@ then be checked against your User entity records in the database: ROLE_USER @@ -307,9 +303,7 @@ then be checked against your User entity records in the database: $container->loadFromExtension('security', array( 'encoders' => array( 'Acme\UserBundle\Entity\User' => array( - 'algorithm' => 'sha1', - 'encode_as_base64' => false, - 'iterations' => 1, + 'algorithm' => 'bcrypt', ), ), 'role_hierarchy' => array( @@ -335,9 +329,9 @@ then be checked against your User entity records in the database: ), )); -The ``encoders`` section associates the ``sha1`` password encoder to the entity +The ``encoders`` section associates the ``bcrypt`` password encoder to the entity class. This means that Symfony will expect the password that's stored in -the database to be encoded using this algorithm. For details on how to create +the database to be encoded using this encoder. For details on how to create a new User object with a properly encoded password, see the :ref:`book-security-encoding-user-password` section of the security chapter. From 3a7020ce0446d6a1a96c868ae7a4cf8353bb335d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 17:49:51 -0600 Subject: [PATCH 2/6] [#3356] Fixing ticks that don't work inside a bold and removing extra details in intro area that aren't necessary (you'll need to read the cookbook entry for more details anyways) --- book/security.rst | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/book/security.rst b/book/security.rst index 66388dbdb1d..319d643fe0a 100644 --- a/book/security.rst +++ b/book/security.rst @@ -660,7 +660,7 @@ see :doc:`/cookbook/security/form_login`. ), ), - **3. Be sure ``/login_check`` is behind a firewall** + **3. Be sure /login_check is behind a firewall** Next, make sure that your ``check_path`` URL (e.g. ``/login_check``) is behind the firewall you're using for your form login (in this example, @@ -1206,19 +1206,6 @@ custom user class is that it implements the :class:`Symfony\\Component\\Security interface. This means that your concept of a "user" can be anything, as long as it implements this interface. -.. versionadded:: 2.1 - In Symfony 2.1, the ``equals`` method was removed from ``UserInterface``. - If you need to override the default implementation of comparison logic, - implement the new :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface` - interface. - -.. note:: - - The user object will be serialized and saved in the session during requests, - therefore it is recommended that you `implement the \Serializable interface`_ - in your user object. This is especially important if your ``User`` class - has a parent class with private properties. - Next, configure an ``entity`` user provider, and point it to your ``User`` class: From dfa761bb4d1658b6786b5f5096cf0dc929e4fbb4 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 18:14:00 -0600 Subject: [PATCH 3/6] [#3356] Changing to use the bcrypt algorithm Also removing one example (now possible with using bcrypt from the beginning) and related tweaks. --- book/security.rst | 113 +++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 71 deletions(-) diff --git a/book/security.rst b/book/security.rst index 319d643fe0a..c1f189ad2a9 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1265,7 +1265,7 @@ in plain text (whether those users are stored in a configuration file or in a database somewhere). Of course, in a real application, you'll want to encode your users' passwords for security reasons. This is easily accomplished by mapping your User class to one of several built-in "encoders". For example, -to store your users in memory, but obscure their passwords via ``sha1``, +to store your users in memory, but obscure their passwords via ``bcrypt``, do the following: .. configuration-block:: @@ -1279,14 +1279,17 @@ do the following: in_memory: memory: users: - ryan: { password: bb87a29949f3a1ee0559f8a57357487151281386, roles: 'ROLE_USER' } - admin: { password: 74913f5cd5f61ec0bcfdb775414c2fb3d161b620, roles: 'ROLE_ADMIN' } + ryan: + password: $2a$12$w/aHvnC/XNeDVrrl65b3dept8QcKqpADxUlbraVXXsC03Jam5hvoO + roles: 'ROLE_USER' + admin: + password: $2a$12$HmOsqRDJK0HuMDQ5Fb2.AOLMQHyNHGD0seyjU3lEVusjT72QQEIpW + roles: 'ROLE_ADMIN' encoders: Symfony\Component\Security\Core\User\User: - algorithm: sha1 - iterations: 1 - encode_as_base64: false + algorithm: bcrypt + cost: 12 .. code-block:: xml @@ -1296,18 +1299,18 @@ do the following: + algorithm="bcrypt" + cost="12" + /> .. code-block:: php @@ -1320,11 +1323,11 @@ do the following: 'memory' => array( 'users' => array( 'ryan' => array( - 'password' => 'bb87a29949f3a1ee0559f8a57357487151281386', + 'password' => '$2a$12$w/aHvnC/XNeDVrrl65b3dept8QcKqpADxUlbraVXXsC03Jam5hvoO', 'roles' => 'ROLE_USER', ), 'admin' => array( - 'password' => '74913f5cd5f61ec0bcfdb775414c2fb3d161b620', + 'password' => '$2a$12$HmOsqRDJK0HuMDQ5Fb2.AOLMQHyNHGD0seyjU3lEVusjT72QQEIpW', 'roles' => 'ROLE_ADMIN', ), ), @@ -1333,71 +1336,35 @@ do the following: ), 'encoders' => array( 'Symfony\Component\Security\Core\User\User' => array( - 'algorithm' => 'sha1', - 'iterations' => 1, - 'encode_as_base64' => false, + 'algorithm' => 'bcrypt', + 'iterations' => 12, ), ), )); -By setting the ``iterations`` to ``1`` and the ``encode_as_base64`` to false, -the password is simply run through the ``sha1`` algorithm one time and without -any extra encoding. You can now calculate the hashed password either programmatically -(e.g. ``hash('sha1', 'ryanpass')``) or via some online tool like `functions-online.com`_ - -.. tip:: - - Supported algorithms for this method depend on your PHP version. - A full list is available calling the PHP function :phpfunction:`hash_algos`. - -.. caution:: - - The above example is not meaned for practical usage, it uses a weak hash - algorithm and it is only done to be able to generate the password easily. Using - :ref:`BCrypt ` is a better option. - .. versionadded:: 2.2 The BCrypt encoder was introduced in Symfony 2.2. -If you're creating your users dynamically (and storing them in a database), -you can use even tougher hashing algorithms and then rely on an actual password -encoder object to help you encode passwords. For example, suppose your User -object is ``Acme\UserBundle\Entity\User`` (like in the above example). First, -configure the encoder for that user: - -.. configuration-block:: - - .. code-block:: yaml +You can now calculate the hashed password either programmatically +(e.g. ``password_hash('ryanpass', PASSWORD_BCRYPT, array('cost' => 12));``) +or via some online tool. - # app/config/security.yml - security: - # ... - - encoders: - Acme\UserBundle\Entity\User: bcrypt - - .. code-block:: xml +.. caution:: - - - + If you're using PHP 5.4 or lower, you'll need to install the ``ircmaxell/password-compat`` + library via Composer: - - + .. code-block:: json - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'encoders' => array( - 'Acme\UserBundle\Entity\User' => 'bcrypt', - ), - )); + { + "require": { + "...": "all the other dependencies...", + "ircmaxell/password-compat": "~1.0.3" + } + } -In this case, you're using the strong ``bcrypt`` algorithm. This means that the -password has been greatly obfuscated so that the hashed password can't be -decoded (i.e. you can't determine the password from the hashed password). +Supported algorithms for this method depend on your PHP version. A full list +is available by calling the PHP function :phpfunction:`hash_algos`. .. versionadded:: 2.2 As of Symfony 2.2 you can also use the :ref:`PBKDF2 ` @@ -1406,10 +1373,11 @@ decoded (i.e. you can't determine the password from the hashed password). Determining the Hashed Password ............................... -If you have some sort of registration form for users, you'll need to be able -to determine the hashed password so that you can set it on your user. No -matter what algorithm you configure for your user object, the hashed password -can always be determined in the following way from a controller:: +If you're storing users in the database and you have some sort of registration +form for users, you'll need to be able to determine the hashed password so +that you can set it on your user before inserting it. No matter what algorithm +you configure for your user object, the hashed password can always be determined +in the following way from a controller:: $factory = $this->get('security.encoder_factory'); $user = new Acme\UserBundle\Entity\User(); @@ -1418,6 +1386,10 @@ can always be determined in the following way from a controller:: $password = $encoder->encodePassword('ryanpass', $user->getSalt()); $user->setPassword($password); +In order for this to work, just make sure that you have the encoder for your +user class (e.g. ``Acme\UserBundle\Entity\User``) configured under the ``encoders`` +key in ``app/config/security.yml``. + .. caution:: When you allow a user to submit a plaintext password (e.g. registration @@ -2070,5 +2042,4 @@ Learn more from the Cookbook .. _`JMSSecurityExtraBundle`: http://jmsyst.com/bundles/JMSSecurityExtraBundle/1.2 .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php -.. _`functions-online.com`: http://www.functions-online.com/sha1.html .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From 0e6cc4d2af6213d08ccadf218cb6d3dbbeba0802 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 18:31:44 -0600 Subject: [PATCH 4/6] [#3356] Moving serialize and Equatable logic further down for clarity --- cookbook/security/entity_provider.rst | 89 ++++++++++++++++----------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 9a7836a5314..481394f1fde 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -32,7 +32,7 @@ The Data Model -------------- For the purpose of this cookbook, the ``AcmeUserBundle`` bundle contains a -``User`` entity class with the following fields: ``id``, ``username``, ``salt``, +``User`` entity class with the following fields: ``id``, ``username``, ``password``, ``email`` and ``isActive``. The ``isActive`` field tells whether or not the user account is active. @@ -77,11 +77,6 @@ focus on the most important methods that come from the */ private $username; - /** - * @ORM\Column(type="string", length=32) - */ - private $salt; - /** * @ORM\Column(type="string", length=64) */ @@ -100,7 +95,6 @@ focus on the most important methods that come from the public function __construct() { $this->isActive = true; - $this->salt = md5(uniqid(null, true)); } /** @@ -116,7 +110,7 @@ focus on the most important methods that come from the */ public function getSalt() { - return $this->salt; + return null; } /** @@ -192,33 +186,15 @@ interface forces the class to implement the five following methods: * ``getRoles()``, * ``getPassword()``, -* ``getSalt()``, +* ``getPassword()``, * ``getUsername()``, * ``eraseCredentials()`` For more details on each of these, see :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface`. -.. sidebar:: What is the importance of serialize and unserialize? - - The :phpclass:`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. The ``id`` is the most important value - that needs to be serialized because the - :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser` - method reloads the user on each request by using the ``id``. In practice, - this means that the User object is reloaded from the database on each - request using the ``id`` from the serialized object. This makes sure - all of the User's data is fresh. - - Symfony also uses the ``username``, ``salt``, and ``password`` to verify - that the User has not changed between requests. Failing to serialize - these may cause you to be logged out on each request. If your User implements - :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, - then instead of these properties being checked, your ``isEqualTo`` method - is simply 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. +If you're curious about the ``serialize`` method or are looking for details +on the logic of seeing if the User stored in the session is the same as the +one stored in the database, see :ref:`cookbook-security-serialize-equatable`. Below is an export of the ``User`` table from MySQL with user ``admin`` and password ``admin`` (which has been encoded). For details on how to create @@ -227,11 +203,11 @@ user records and encode their password, see :ref:`book-security-encoding-user-pa .. code-block:: bash $ mysql> select * from acme_users; - +----+----------+------+------------------------------------------+--------------------+-----------+ - | id | username | salt | password | email | is_active | - +----+----------+------+------------------------------------------+--------------------+-----------+ - | 1 | admin | | d033e22ae348aeb5660fc2140aec35850c4da997 | admin@example.com | 1 | - +----+----------+------+------------------------------------------+--------------------+-----------+ + +----+----------+------------------------------------------+--------------------+-----------+ + | id | username | password | email | is_active | + +----+----------+------------------------------------------+--------------------+-----------+ + | 1 | admin | d033e22ae348aeb5660fc2140aec35850c4da997 | admin@example.com | 1 | + +----+----------+------------------------------------------+--------------------+-----------+ The next part will focus on how to authenticate one of these users thanks to the Doctrine entity user provider and a couple of lines of @@ -743,3 +719,46 @@ fetch the user and their associated roles with a single query:: The ``QueryBuilder::leftJoin()`` method joins and fetches related roles from the ``AcmeUserBundle:User`` model class when a user is retrieved by their email address or username. + +.. _`cookbook-security-serialize-equatable`: + +Understanding serialize and how a User is Saved in the Session +-------------------------------------------------------------- + +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, 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 in some way 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 :phpclass:`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. Only the ``id`` needs to be serialized, +because the :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser` +method refreshes the user on each request by using the ``id`` (as explained +above). In practice, this means that the User object is reloaded from the +database on each request using the ``id`` from the serialized object. This +makes sure all of the User's data is fresh. + + +Symfony also uses the ``username``, ``salt``, and ``password`` to verify +that the User has not changed between requests. Failing to serialize +these may cause you to be logged out on each request. If your User implements +:class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, +then instead of these properties being checked, your ``isEqualTo`` method +is simply 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. + +.. versionadded:: 2.1 + In Symfony 2.1, the ``equals`` method was removed from ``UserInterface`` + and the ``EquatableInterface`` was added in its place. From 1eefb1b33da12f163f5110bfc71750c161711e05 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 18:55:53 -0600 Subject: [PATCH 5/6] [#3356] Clarifying when you need a salt Also filling in other details related to using BCrypt --- book/security.rst | 14 +------- .../_ircmaxwell_password-compat.rst.inc | 13 +++++++ cookbook/security/entity_provider.rst | 35 +++++++++++++------ 3 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 cookbook/security/_ircmaxwell_password-compat.rst.inc diff --git a/book/security.rst b/book/security.rst index c1f189ad2a9..59432b994d9 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1349,19 +1349,7 @@ You can now calculate the hashed password either programmatically (e.g. ``password_hash('ryanpass', PASSWORD_BCRYPT, array('cost' => 12));``) or via some online tool. -.. caution:: - - If you're using PHP 5.4 or lower, you'll need to install the ``ircmaxell/password-compat`` - library via Composer: - - .. code-block:: json - - { - "require": { - "...": "all the other dependencies...", - "ircmaxell/password-compat": "~1.0.3" - } - } +.. include:: /cookbook/security/_ircmaxwell_password-compat.rst.inc Supported algorithms for this method depend on your PHP version. A full list is available by calling the PHP function :phpfunction:`hash_algos`. diff --git a/cookbook/security/_ircmaxwell_password-compat.rst.inc b/cookbook/security/_ircmaxwell_password-compat.rst.inc new file mode 100644 index 00000000000..20764184e3d --- /dev/null +++ b/cookbook/security/_ircmaxwell_password-compat.rst.inc @@ -0,0 +1,13 @@ +.. caution:: + + If you're using PHP 5.4 or lower, you'll need to install the ``ircmaxell/password-compat`` + library via Composer in order to be able to use the ``bcrypt`` encoder: + + .. code-block:: json + + { + "require": { + "...": "all the other dependencies...", + "ircmaxell/password-compat": "~1.0.3" + } + } \ No newline at end of file diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 481394f1fde..68c5430c323 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -95,6 +95,8 @@ focus on the most important methods that come from the public function __construct() { $this->isActive = true; + // may not be needed, see section on salt below + // $this->salt = md5(uniqid(null, true)); } /** @@ -110,6 +112,8 @@ focus on the most important methods that come from the */ public function getSalt() { + // you *may* need a real salt depending on your encoder + // see section on salt below return null; } @@ -144,8 +148,9 @@ focus on the most important methods that come from the return serialize(array( $this->id, $this->username, - $this->salt, $this->password, + // see section on salt below + // $this->salt, )); } @@ -157,19 +162,13 @@ focus on the most important methods that come from the list ( $this->id, $this->username, - $this->salt, $this->password, + // see section on salt below + // $this->salt ) = unserialize($serialized); } } -.. note:: - - If you choose to implement - :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, - you determine yourself which properties need to be compared to distinguish - your user objects. - .. tip:: :ref:`Generate the database table ` @@ -186,7 +185,7 @@ interface forces the class to implement the five following methods: * ``getRoles()``, * ``getPassword()``, -* ``getPassword()``, +* ``getSalt()``, * ``getUsername()``, * ``eraseCredentials()`` @@ -213,6 +212,20 @@ The next part will focus on how to authenticate one of these users thanks to the Doctrine entity user provider and a couple of lines of configuration. +.. sidebar:: Do you need to use a Salt? + + Yes. Hashing a password with a salt is a necessary step so that encoded + passwords can't be decoded. However, some encoders - like Bcrypt - have + a built-in salt mechanism. If you configure ``bcrypt`` as your encoder + in ``security.yml`` (see the next section), then ``getSalt()`` should + return ``null``, so that Bcrypt generates the salt itself. + + However, if you use an encoder that does *not* have a built-in salting + ability (e.g. ``sha512``), you *must* (from a security perspective) generate + your own, random salt, store it on a ``salt`` property that is saved to + the database, and return it from ``getSalt()``. Some of the code needed + is commented out in the above example. + Authenticating Someone against a Database ----------------------------------------- @@ -311,6 +324,8 @@ the database to be encoded using this encoder. For details on how to create a new User object with a properly encoded password, see the :ref:`book-security-encoding-user-password` section of the security chapter. +.. include:: /cookbook/security/_ircmaxwell_password-compat.rst.inc + The ``providers`` section defines an ``administrators`` user provider. A user provider is a "source" of where users are loaded during authentication. In this case, the ``entity`` keyword means that Symfony will use the Doctrine From 473f8c19c8fe48439a89abab6a5eccddf45a9ea1 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 20 Jan 2014 20:41:59 -0600 Subject: [PATCH 6/6] [#3405] Tweaks thanks to @WouterJ and @xabbuh --- .../security/_ircmaxwell_password-compat.rst.inc | 4 ++-- cookbook/security/entity_provider.rst | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/cookbook/security/_ircmaxwell_password-compat.rst.inc b/cookbook/security/_ircmaxwell_password-compat.rst.inc index 20764184e3d..3f96c454488 100644 --- a/cookbook/security/_ircmaxwell_password-compat.rst.inc +++ b/cookbook/security/_ircmaxwell_password-compat.rst.inc @@ -7,7 +7,7 @@ { "require": { - "...": "all the other dependencies...", + ... "ircmaxell/password-compat": "~1.0.3" } - } \ No newline at end of file + } diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 68c5430c323..b6919839b83 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -740,8 +740,8 @@ address or username. Understanding serialize and how a User is Saved in the Session -------------------------------------------------------------- -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 +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 @@ -757,18 +757,17 @@ Even though this all happens automatically, there are a few important side-effec First, the :phpclass:`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. Only the ``id`` needs to be serialized, +but it's probably a good idea. In theory, only the ``id`` needs to be serialized, because the :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser` method refreshes the user on each request by using the ``id`` (as explained -above). In practice, this means that the User object is reloaded from the -database on each request using the ``id`` from the serialized object. This -makes sure all of the User's data is fresh. - +above). However in practice, this means that the User object is reloaded from +the database on each request using the ``id`` from the serialized object. +This makes sure all of the User's data is fresh. Symfony also uses the ``username``, ``salt``, and ``password`` to verify that the User has not changed between requests. Failing to serialize these may cause you to be logged out on each request. If your User implements -:class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, +the :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, then instead of these properties being checked, your ``isEqualTo`` method is simply called, and you can check whatever properties you want. Unless you understand this, you probably *won't* need to implement this interface