Search code examples
symfonyfosuserbundleimpersonation

Symfony impersonation - separate firewalls and separate user providers


I have a Symfony application with two firewalls, one for admins and one for normal users.

admin:
    provider: admin
    # etc

main_site:
    form_login:
        provider: fos_userbundle
        csrf_provider: form.csrf_provider

I'd like admin users to be able to impersonate normal users. How can I do this, given that they're using separate firewalls and separate user providers?


Solution

  • There were several things I had to do to get this to work.

    Context key: As described here, I had to give both firewalls the same context. Without this, admins were taken to the login page when trying to switch users.

    Config on both firewalls: I had to add the basic switch_user configuration keys to both firewalls:

    switch_user:
        role: ROLE_ADMIN
    

    If I just put the config on the main_site firewall, admins got an access denied message when exiting impersonation and going to an admin page. (For example, the route /admin/dashboard?_switch_user=_exit would give a 403).

    Provider key on the main_site's config:

    main_site:
        switch_user:
            role: ROLE_ADMIN
            provider: fos_userbundle
    

    Without this, I got the error "Switch User failed - user@example.com not found". Digging into the code, it turned out that the admin user provider was being used, and of course the normal users couldn't be found when using that provider.

    (provider key for switch_user config discussed here.)

    Alternatively, I could have added this as a provider key for the firewall itself:

    main_site:
        switch_user:
            role: ROLE_ADMIN
        provider: fos_userbundle
    

    You'll see from the config in my question that fos_userbundle was only specified as a provider for form_login, not for main_site as a whole, which is why it wasn't being used until I added it. Adding it in either place (impersonation config or whole firewall) would do the trick.

    Here's the full set of relevant config:

    admin:
        provider: admin
        # Have to put basic switch_user config on both firewalls
        switch_user:
            role: ROLE_ADMIN
        # Both the admin and main_site firewalls have the same context, to allow
        # cross-firewall impersonation
        # https://stackoverflow.com/a/17991481/328817
        context: boardworks
    
    main_site:
        form_login:
            provider: fos_userbundle
            csrf_provider: form.csrf_provider
        switch_user:
            role: ROLE_ADMIN
            # Have to explicitly set the provider, otherwise the site will use the admin
            # user provider when looking up the users whom admins are trying to impersonate
            provider: fos_userbundle
        # Rather than adding the provider above, I could have added it here:
        #provider: fos_userbundle