Search code examples
symfonyfosuserbundle

Log out of a switched user programmatically


I have an application where the administrator often acts on behalf of the regular users. The flow is as follows:

  • Admin logs on
  • Admin is presented with a list of non-admin users
  • Admin clicks a link to a non-admin user <a href="{{ path('_main_app', {'_switch_user': user.username}) }}">
  • Admin does their thing as the user
  • Admin clicks browser back until they are at the list of non-admin users again

At this point the admin page shows a button <a href="{{ path('_admin', {'_switch_user': '_exit'}) }}">Exit {{app.user.username}} </a>

However, the admin users often forget to click it and instead click on another non-admin user. When they do this they get a message You are already switched to User XXX. Obviously this is intended, but it is still not a great experience for the admin users.

What would be the recommended approach for allowing the admins to switch users in this way? I would think that calling {'_switch_user': '_exit'} every time they reach the list of normal users would be good option (along with forcing this page never to be cached), but I can’t see a way to do that programmatically.

Another way might be to visit the URL {'_switch_user': '_exit'} in Javascript, but that seems very unsatisfactory.

What would the recommended approach be?


Solution

  • You should take a look at the SwitchUserListener Class (Symfony\Component\Security\Http\Firewall\SwitchUserListener).

    This is where the magic happens (and the "you are already switched to... message is created"). The problem is it doesn't give you really that much options to hook into it. Actually the only official one is the event after a successful switch.

    But you are able to replace this Listener completely with your own implementation. Used that in my own project to implement ip restrictions for a user switch (acls didn't work or i got it wrong)

    Simply create your own and replace it in your services.yml

    security.authentication.switchuser_listener:
        class: AppBundle\Services\Security\SwitchUserListener
        public: false
        abstract: true
        arguments: ['@security.token_storage', '', '@security.user_checker', '', '@security.access.decision_manager', '@?logger', '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', '@?event_dispatcher']
        tags:
            - { name: monolog.logger, channel: security }
    

    Edit: You will need to copy the whole thing though since all the relevant functions are private. It's not pretty but i don't see any other way (except in your usecase maybe don't show the switch-link at all while the user is already switched.)