Adding Remember Me Functionality to FOSUserBundle
In this video we will implement the remember_me
functionality, allowing your Users to remain logged in for longer than the length of your server's session expiry time.
Security is a tough topic to get right. This is one of the huge benefits of using a framework like Symfony2. We get to share in the vast wealth of experience of its creators and contributors, and gain a security implementation that has been peer reviewed by the communities security experts.
Remember Me functionality is achieved through a cookie with a specific expiry date, stored in your user's browser.
The expiry date is usually longer than the length of a session on the server. The session expiry length can be changed in your php.ini
file, but by default it will be 1440 seconds / 24 minutes. An odd amount of time.
This way, when the user's session expires, they still remain logged in to your website, until their Remember Me cookie expires.
I am assuming at this stage that you have not customised your Session Handler:
# app/config/config.yml
framework:
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
If you are curious what you session expiry time is set to, try:
php -i | grep "session.gc_maxlifetime"
Remember this value may be different on your development and production servers. If you want to read more on Symfony's Session Configuration options, this link is very helpful, and this link goes into more technical depth.
One other source of potential bugs when working with sessions - if you are on a shared server, or a server with multiple running PHP-based websites - is that the PHP session directory is shared, unless otherwise specified.
Therefore, you can end up with one website triggering garbage collection of the shared session directory, and removing sessions from another website. Tricky to debug - so consider explicitly setting your session save_path
if on a shared server.
Remember Me Firewall
Remember Me functionality is not set up in FOSUserBundle as part of the default installation instructions.
This is confusing as the FOSUserBundle template for sign in does come with the remember_me
checkbox already added.
You are expected to add in the remember_me
firewall to your security.yml
file if you want this feature enabling.
Fortunately, this is largely just copy / paste from the Symfony documentation:
# app/config/security.yml
security:
# ...
firewalls:
default:
# ...
remember_me:
key: "%secret%"
lifetime: 604800 # 1 week in seconds
path: /
Notice the use of "%secret%"
, which is pretty cool - we are pulling the secret
parameter from our app/config/parameters.yml
file for use here.
key
is the only configuration option we must provide.
There are a number of other useful options available, so as ever, be sure to read the documentation.
Gotcha - Requiring a Previous Session
One of the strange things I am yet to fully understand is why we would need to have an existing session to be able to log in.
This one left me scratching my head, and I am yet to find an answer that makes sense to me - so if you know why this is, please leave a comment.
After adding a remember_me
firewall entry, you may hit an error when logging in that says:
"No session available, it either timed out or cookies are not enabled"
To fix this, you need to add require_previous_session: false
to your form_login
config for whichever firewall you are working with (main
by default):
# app/config/security.yml
security:
# ...
firewalls:
main:
form_login:
# ...
require_previous_session: false
This should resolve that problem.
Using the Remember Me Parameter
If you have been following along so far, you may have copied and pasted the Bootstrap 3 sign in template and then copied and pasted the bits and pieces from the FOSUserBundle login.html.twig
template back in as appropriate.
There is a little gotcha to be aware of here.
The remember_me_parameter
in Symfony, by default, is going to be _remember_me
.
This needs to be set as the name
property on your Remember Me checkbox.
If you have copied and pasted from Bootstrap, there is a chance you might have missed this. It is pretty easy to overlook.
Remember, this is the name
property, not the id
property - another easy mistake to make.
<!-- /vendor/friendsofsymfony/user-bundle/Resources/views/Security/login.html.twig -->
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />
<label for="remember_me">{{ 'security.login.remember_me'|trans }}</label>
Becomes:
<!-- app/Resources/FOSUserBundle/views/Security/login.html.twig -->
<div class="checkbox">
<label>
<input type="checkbox"
id="remember_me"
name="_remember_me"
value="on" /> {{ 'security.login.remember_me'|trans }}
</label>
</div>
Testing It Works
Testing the Remember Me functionality is working on your site is relatively easy.
Once you have set up your configuration as required, if you open your Chrome Developer Tools, then go to the 'Resources' tab, and open up the 'Cookies' section, you should see a cookie with the name of REMEMBERME
(you can change that) and an expiry date set in line with the value you have configured for your remember_me
lifetime.
How Does This Affect Roles?
One further point to understand is how remember_me
affects your user's Roles.
Symfony has three different Roles that are automatically available on a User's object, depending on their authentication status:
IS_AUTHENTICATED_ANONYMOUSLY
Automatically assigned to a user who is in a firewall protected part of the site but who has not actually logged in. This is only possible if anonymous access has been allowed.
IS_AUTHENTICATED_REMEMBERED
Automatically assigned to a user who was authenticated via a remember me cookie.
IS_AUTHENTICATED_FULLY
Automatically assigned to a user that has provided their login details during the current session.
(Taken directly from the Symfony docs)
These roles are stacked. If you have IS_AUTHENTICATED_REMEMBERED
, then you also have IS_AUTHENTICATED_ANONYMOUSLY
, and likewise, IS_AUTHENTICATED_FULLY
is stacked on top of IS_AUTHENTICATED_REMEMBERED
and IS_AUTHENTICATED_ANONYMOUSLY
.
This is cool as it allows you to be more restrictive on Users who are only authenticated using their remember_me
cookie - allowing you to perhaps show your User details to a User, but only allow updating / changing those details if the User has actively authenticated this session.
More Reading
Aside from the links throughout this page, I would recommend reading: