Impersonating a Different User


In this video we will implement the ability to quickly Switch / Impersonate Users inside your Symfony application.

This feature is incredibly useful if one of your Users has a problem which is proving difficult to replicate, and you would like to be able to log in as that User and see the issue for yourself.

What's cool about being able to Impersonate a User in Symfony is that you won't need to reset their password, or know their password at all.

As long as you are logged in with a User account that has the required Role, you will be able to switch to, or impersonate any(*) User on your system.

* - sometimes being able to impersonate any User is not what you would want. For example, having a helpdesk member being able to impersonate standard Users is good. Having that same helpdesk member being able to impersonate a Super Admin account? Baddddd. If you want to restrict this down further, start off with this post, and I also recommend Joshua's Security Deep Dive book.

Setting Up User Switching

There is a full Symfony Cookbook article on User Impersonation, so be sure to head over there if unsure of any of this.

The ability to switch Users is enabled per Firewall. This is only going to be an issue if you have multiple firewalls in your Symfony application. Remember, this refers to Symfony's firewalls, not your network firewall.

As with most things Symfony, there are multiple ways to configure User impersonation. There is the simple way, and the more configurable way.

The Simple Way

The simplest way to enable User Impersonation inside Symfony is to add switch_user: true to your firewall:

# app/config/security.yml
security:
    # ...

    firewalls:
        main:
            # ...
            switch_user: true

This will enable User switching with the default settings.

The default settings mean you will switch users with the URL parameter of _switch_user={username} and you will need to have the Role of ROLE_ALLOWED_TO_SWITCH on your User account.

That is to say, if your site was your-site.com, you could switch by browsing to the URL:

http://your-site.com/?_switch_user=peter

Assuming all is well, you will find yourself logged in as 'peter', until you browse to:

http://your-site.com/?_switch_user=_exit

Which will log you out of 'peter', leaving you logged in as yourself once more.

The More Configurable Way

You don't have to accept the default URL parameter, and you can change up the Role which gives access to switch_user.

You can even customise the user_provider property which allows you to configure a completely custom solution to returning Users authorised to switch.

To enable the more 'advanced' options, rather than pass in true, instead we can specify the properties we want:

# app/config/security.yml
security:
    # ...

    firewalls:
        main:
            # ...
            switch_user:
                provider:             ~
                parameter:            _switch_user
                role:                 ROLE_ALLOWED_TO_SWITCH  

You can change the required Role, or the parameter we need to pass in to the URL to make the switch happen.

Switching Back

Once you have switched to a different User, we have seen how we can pass in the special variable of _exit to return back to the previous User / yourself:

http://your-site.com/?_switch_user=_exit

Rather than making our poor Users remember this syntax, we can take advantage of how Symfony handles User Impersonation behind the scenes.

Whenever we successfully switch to a different User, we will get all their Roles, and in addition we will get given the ROLE_PREVIOUS_ADMIN role also.

Inside our Twig template, we could check whether the current User has this Role, and if so, display them a link to exit impersonation:

{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
    <a href="{{ path('homepage', {'_switch_user': '_exit'}) }}">Exit impersonation</a>
{% endif %}

As we have been using Bootstrap so far in this series, we could improve upon this and add this snippet to the Topnav / Navbar we created in the previous video:

<!-- app/Resources/FOSUserBundle/views/topnav.html.twig -->

<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <!-- *snip* -->
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav navbar-right">

                {% if is_granted('ROLE_PREVIOUS_ADMIN') %}
                <li>
                    <a href="{{ path('homepage', {'_switch_user': '_exit'}) }}">Exit impersonation</a>
                </li>
                {% endif %}

                {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
                    <!-- *snip* -->
                {% else %}
                    <!-- *snip* -->
                {% endif %}
            </ul>
        </div><!--/.nav-collapse -->
    </div>
</nav>

Notice, this {% if is_granted('ROLE_PREVIOUS_ADMIN') %} section is separate from the already existing {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} block we defined previously.

Now, only when we are in User Impersonation mode, we will get an additonal item added to our Bootstrap Navbar with some nice styling.

Code For This Course

Get the code for this course.

Episodes