Creating a backdoor login

I’m writing a large app which has two separate areas that folks log in to:

The site login area is for my customers to log into, to manage their accounts. I refer to this as the customer "portal".

The administrative login area, (a module named "admin"), is where I log in, to manage my customers.

I’d like to build a backdoor such that, if someone is logged in to the “admin” module, they can click on a customer’s account name and presto, be logged in that customer’s “portal”.

Have any of you solved this up–with an elegant solution you’re willing to share?

Thanks in advance!

And why don’t you use RBAC? One login with different roles.

create a controller/action that only admin will have access to, on the user view you could av a link that is only visible to admin that points to that action.

in the action

check again if current user is admin.

if true, authenticate the current user as the user u want to become

Jay - thanks so much, your answer was spot on. The solution below works.

Again, what this "backdoor" procedure does is to grant an authenticated user in the "admin" module access – to ANY account desired – in the "portal" module.

See my questions at the bottom–would love advice from security experts.

Steps involved:




// 1. AdminModule::init().  Set stateKeyPrefix to 'admin':

public function init()   {

   // import the module-level models and components

   $this->setImport(array(

     		    'admin.models.*',

		    'admin.components.*',

		));


   Yii::app()->setComponents(array(

      'user'=>array(

       'class'=>'CWebUser',

       'stateKeyPrefix'=>'admin',		          

       'loginUrl'=>Yii::app()->createUrl('admin/default/login'),

       'returnUrl'=>Yii::app()->createUrl('admin/'),

)));


}


// -----------------------------------------------------------------------

// 2. Modify the portal authentication method -- UserIdentity.php::init()

// -----------------------------------------------------------------------


/**

 * Authenticates the user with name given by $this->username -- or grants backdoor access.

 * @param $backdoorAs - string - If non-null, then "Authenticate" as the portal user

 *         with the name $backdoorAs. I.e., we won't truly authenticate, we'll

 *         instead grant access as long as the currently logged-in user is 

 *         logged in to *admin* module.

 * @return boolean whether authentication succeeds.

 *

 * NOTE: backdoor authentication will NEVER work if you logged in using the actual 

 * login process. LOG OUT of the portal first.

 */

public function authenticate($backdoorAs=null)

{

   $this->errorCode=self::ERROR_NONE;


   $backdoor = $backdoorAs !== null;  // for readability

   if ($backdoor)   {

	$this->username = $backdoorAs;

   }

	   

   $user=User::model()->find('LOWER(username)=?',array(strtolower($this->username)));

   if($user===null) 

      $this->errorCode=self::ERROR_USERNAME_INVALID;


    else {

	// Backdoor authentication. Allow login if admin session var 'admin__id'

        // is non-zero. (Session keys are prepended with the string 'admin' because

        // that's how we set up the stateKeyPrefix in AdminModule.php, above.)

	if ($backdoor) {

	   $adminId = Yii::app()->session->itemAt('admin__id');

	   if ($adminId === null || $adminId < 1)   {

              $this->errorCode=self::ERROR_USERNAME_INVALID;

           }

        }

        // Else, the usual portal login, validating password

        else if (!$user->validatePassword($this->password))

	   $this->errorCode=self::ERROR_PASSWORD_INVALID;

   }

      

   //  User is authentic.

   if ($this->errorCode == self::ERROR_NONE) {

	$this->_id=$user->id;

	$this->username=$user->username;

   }

   return $this->errorCode==self::ERROR_NONE;

}


// -----------------------------------------------------------------------

// 3. Create a controller for the "portal" module:

// -----------------------------------------------------------------------


/**

 * Controller for backdooring in, from admin, to the portal.

 */

class BackdoorController extends Controller

{

   /**

    * If the current user is an admin user, who has successfully authenticated, and if

    * the variable "user" is present as part of the request, then allow this user to view the portal,

   * as if logged in as the user specified in the query string.

 */

   public function actionIndex()

   {

      $user= Yii::app()->request->getQuery('user', '');

      // If no user, or empty user, quietly redirect. 

      if (empty($cpanelUser))   {

          $this->redirect(Yii::app()->createUrl('portal/'));

	   return;

	}


	$password = '';

	$identity=new UserIdentity($user, $password);

	if ($identity->authenticate($user)) {

	   $duration= 2592000;  // 3600*24*30 = 30 days

	   Yii::app()->user->login($identity, $duration); // BACKDOOR

	}

	$this->redirect(Yii::app()->createUrl('portal/'));


      } else  {

          // Authentication failed. Quietly redirect. 

	  $this->redirect(Yii::app()->createUrl('portal/'));

      }

   }

}




// -----------------------------------------------------------------------

// 4. Create links to the backdoor, in views, etc., in the "admin" module

// -----------------------------------------------------------------------


echo CHtml::link('login as sue', 

                 Yii::app()->createUrl('portal/backdoor', array('user'=>'sue')));






If anyone can add some measures of SECURITY to this implementation, I would appreciate it. My worry, specifically, is that a portal user could guess the backdoor URL :

index.php/portal/backdoor/user/sue

If they were to guess that URL, would it be possible for them to fake the admin__id SESSION variable such that they could gain access to sue’s portal? If so, what can I do to make this more secure?

:)

In BackdoorController::actionIndex you ensure that the current user is authenticated and is a authorized to perform the action.

Hmm, not sure what you mean. Notice that I actually authenticate in UserIdentity::authenticate(). We look at the session, checking to see if there’s a session variable “admin__id”. If so, and if it’s non-zero, we consider the user session to be a valid “admin” session. My question has to do with just how secure a method of authentication that is. Anyone?

Some other approach:

I’ve successfully configured 2 webusers in my applications, using a different stateKeyPrefix for the second user. One is for the admin section the other for the frontend section. Now both can login/logout independently.

To have control over the frontend login from the admin section, you could add a static method to your UserIdentity, that creates an authenticated identity without requiring a password (see here). You could use this method in the admin area to create an authenticated fronted identity and use this to force a login as frontend user.

Hi, I am also currently developing two major huge projects and one of them will require the Front end for web users and the admin panel that I am also thinking to create it (for my first time) as a module. The admin module panel will be for different type of users and that I can easily handle with rights or roles or whatever, my question is about the front end guys and unifying authentication. What is the best approach? Best practices?

I will have two login areas (user / partners | admin), user will be redirected to profile front-end, partners | admin back-end.

I have seen a couple of solutions, and I think I may know what I can do, but I really would like to know if I am on the right track.

Thanks in advance

Hey Antonio – I haven’t seen any updates on this topic – do you have any? Interested to know what you’ve learned! ;)

Hi Antonio,

I wanted to the same as of yours.

Can you please update whether you have completed the task, and share us the resource.

Thanks