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:

Code For This Course

Get the code for this course.

Episodes