User-Switch for CakePHP apps

What I call a user-switch is nothing more than a simple select box to chose a user from with which you want to be logged in as.

Why

This can be a very useful tool and time saver for multiple reasons. You might want to quickly edit/see the frontend just as any of your users, for example. In most cases the password itself is a personal one and only known to the user itself (which is fine!). Therefore an admin can safely jump into a user’s account without compromising the password here. But please read disclaimer below (final notes).

Basic idea

First of all you need an admin view or a sidebar element containing a select box of your users and a post button submitting to your switch action.
This select box should only be visible to you if you are currently logged in as admin.
After submit this action then overwrites the session date for Auth. After the redirect you should be logged in as this new user just as if you used the login form.
But you did not have to know the password or use any master password solution here which can be potential security issues. You are already verified as authenticated role and this renders it safer then those other approaches where just about anybody could try to trigger the switch.

Using this action you can also switch back to your original account again.

Tips

After using it for several years now in multiple different ways and apps I will try to outline a few sticking points.

You should store an Admin.id in your session Auth data to "know" if you are the real user or the fake (switched) one.
This is handy if you do NOT want to trigger certain things like "online activity update" or "message read" etc which only the real user should IMO. this way you can prevent this.

Also make sure only the admin role that will do the initial switch has access to the switch action. You can also allow the Admin.id access, of course (if you want to be able to switch back again).

You can add -1 as default value in your select box (at the very top for example) to switch back to the own account and handle that separately in your switch action.

After a successful change don’t forget to redirect to a page all roles can access to prevent any redirect loops here.

Implementation

The quickest way would be to use my Tools Plugin and include the CommonComponent in your public $components array.
Alternatively you could also use my DirectAuthenticate directly – or even Auth->login() manually. But for the sake of simplicity here my DRY approach:

You need a switch action in your users controller:

public function switch_user() {
if ($this->Common->isPosted()) {
    $id = $this->request->data['User']['id'];
    if ($this->Common->manualLogin($id)) {
        // success message and redirect
    }
    // error message and referer redirect back to the action we posted from
}

The manualLogin() method can automatically log in the user just as Auth->login($user) would. Only that the first one is a cleaner approach regarding settings such as scope and contain which will automatically be transferred over from the Auth->authenticate settings. So if you contain Role and Customer relations for example, the logged in user via manualLogin will always have the same session data just as he would by normally logging in. This is IMO more robust than using find() and login() where you might end up forgetting to adjust the find options here due to the code redundancy.

Even if you don’t want to use any of my code, the basic idea of how it works should still be clear. The main point here is that "swapping" the Auth data in your session makes it possible to jump.

More advanced implementation

Cake2.3 using onlyAllow():

$this->request->onlyAllow('post');
if (!$isAdminRole && !$this->Session->check('Auth.Admin.id')) {
    throw new MethodNotAllowedException(__('Access not allowed'));
}

$formerId = $this->Session->read('Auth.User.id');
$id = $this->request->data['User']['id'];
if ($this->Common->manualLogin($id)) {
    if (!$this->Session->check('Auth.Admin.id')) {
        $this->Session->write('Auth.Admin.id', $formerId);
    } elseif ($this->Session->read('Auth.Admin.id') == $formerId) {
        $this->Session->delete('Auth.Admin.id');
    }
    // success message and redirect
}
// error message and referer redirect back to the action we posted from

Using the Auth.Admin.id we can easily find out when we are back as the original user and remove this session data. This way everything is back to the way it was before.
We also assert that only the admin (switched or not) has access. Don’t forget to manually populate $isAdminRole here with your (ACL) way of retrieving this information.

Final notes

This feature should not be used for social network sites or other similar apps where there will be "private" user content. You can use this for websites, where you got employees, for example, which all are aware of the fact that the admin can temporally take over the user account and the users do not store sensitive information or send private messages themselves.

This is also very helpful in testing and during development. Just make sure you only display and allow switching with debug mode on or with env(‘REMOTE_ADDR’) === ‘127.0.0.1’ etc.

Have fun 🙂

6 Comments

  1. This solution may be extremely useful in e-commerce solution to see exactly what a client is viewing on it’s account!

    Bye!

  2. Hi,
    It seems that you removed the manualLogin() method from your CommonComponent. Could you post the code somewhere please, if you still have it ?
    Thanks!

  3. Did you check the 2.x codebase? I am sure it was in there, and wasn’t ported to 3.x.
    In 3.x you have other ways to manually log in now.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.