Yii2 remote logout a user session from the user current session - php

I wanted to logout a user session logged in a different browser/environment from the current session of the same user. A feature similar to this - https://www.facebook.com/settings?tab=security&section=sessions&view.
Yii2 is the backend framework used. Using redis for session management - yii2-redis. I also save the session ids saved in database.
I followed this article -
http://www.codeinphp.com/general/php-code-snippets/remotely-destroy-different-session-php-forced-user-signout/
But didn't had any success.
session_id($old_session_id);
session_start(); // This line throws error.
session_destroy();
Deleting the key in redis using \Yii::$app->session->destroySession($oldSessionId) didn't logout.
Changing the session id to old one and then destroying the session also didn't work.
$currentSessionId = \Yii::$app->session->getId();
\Yii::$app->session->setId($oldSessionId);
\Yii::$app->getSession()->destroy();
\Yii::$app->session->setId($currentSessionId);
If anyone had success in implementing this successfully, please share your solution. Also if there is any documentation regarding this which can help, please do provide.

The first, session_start() must be call before session_id() and just call only once
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
session_id($old_session_id);
session_destroy();
But just remove session, that is not enough if you allow user auto login because browser will auto login by using cookies. To solve, you must change user AuthKey - Yii2 use AuthKey to validate user auto login. By default each user have only one AuthKey in user table so that when you change AuthKey user logout anywhere. So we have to custom. Create for each user session an AuthKey, stored somewhere not in user table. Do it easy: extends yii\web\User class override afterLogin function to create AuthKey for each login session. override validateAuthKey function to validate auto login use our custom AuthKey. Now when you want to kill any user session : kill PHP session id and AuthKey that session will be logout right away.
I have been using this solution for my projects and it works fine.

Based on Ngo's answer, I figured out a method that works well and is easier to setup.
1) Add a "last_session_id" field to your user table.
2) Add following to your main controller:
public function afterAction($action, $result)
{
$result = parent::afterAction($action, $result);
if(Yii::$app->user->id)
{
//update the user table with last_session_id
$user = User::find()->where(['id' => Yii::$app->user->id])->one();
$user->last_session_id = Yii::$app->session->id;
$user->save(false);
}
return $result;
}
3) Change site/login action to following:
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
//delete previous session ID and change auth key
Yii::$app->session->destroySession(Yii::$app->user->identity->last_session_id);
$u = \common\models\User::find()->where(['id' => Yii::$app->user->id])->one();
$u->auth_key = Yii::$app->security->generateRandomString();
$u->save(false);
return $this->goBack();
} else {
return $this->render('/site/login', [
'model' => $model,
]);
}
}

Related

Laravel 5.6: Force logout remember me login

I have a custom login controller in my app, which prevents multiple sessions per user. It logs out the user if they log in from another device/browser:
public function authenticated(Request $request, $user) {
$previous_session = $user->session_id;
if ($previous_session) {
session()->getHandler()->destroy($previous_session);
}
auth()->user()->session_id = session()->getId();
auth()->user()->save();
return redirect(session()->pull('from', $this->redirectTo));
}
Regardless of the session driver, this code looks for a session_id on users table and destroys the session associated with it.
But this doesn't work if the user logged in with remember me checkbox enabled. User stays logged in with previous device/browser. How can I tell laravel to forget this remember me after the second login? Thanks.
You have to invalidate or cycle the remember_token in the users database table.
This happens automatically when you call Auth::logout().
The remember_token can also be invalidated manually similiar to how Laravel handles it:
protected function cycleRememberToken(AuthenticatableContract $user)
{
$user->setRememberToken($token = Str::random(60));
$this->provider->updateRememberToken($user, $token);
}
Source: laravel/framework/src/Illuminate/Auth/SessionGuard.php

laravel event listener for session expire, as no logout event listener called on session expiry

the scenario is simple: i want a single user to be logged in from only one device at a given time. using laravel framework version 5.2
here is my work so far to achieve this, using event listeners:
i set a is_logged_in flag in users table which is ON when user logs in. and based it its ON status it will reject all the subsequent request of log in from same user.
i used login attempt, login and logout events to achieve this:
on login attempt listener the following code is executed. it will check if the user is already logged in then a flag logged is set in session.
$user = User::select('is_logged_in')->where('email', $event->credentials['email'])->first();
if($user->is_logged_in == 1){
Session::put('logged', 'Yes');
}else{
Session::put('logged', 'No');
}
now at login event, the session flag logged is checked, if its set then user is forcefully logout, maintaining the remember_token (which is changed on logout)
if(session('logged') == "Yes"){
$previous_remember_token = $event->user->remember_token;
$previous_usesr_id = $event->user->id;
Auth::logout();
$user = User::find($previous_usesr_id);
$user->remember_token = $previous_remember_token;
$user->save();
}else{
$event->user->is_logged_in = 1;
$event->user->save();
}
and in logout even i used following code to reset user database flag of is_logged_in:
if(session('logged') != "Yes"){
$event->user->is_logged_in = 0;
$event->user->save();
}
now here are the issues im facing:
When session expires, logout even is not executed, how can i execute my logout event code on session expire? is there any session:expire event listener present in laravel?
1.1 This also leads to another question what how laravel monitor session time, and where it saves it? how and when laravel framework make the session as expired?
i also want to execute the logout code when the user close the tab or browser, which is really hard but i think may be some ajax function updating the users table column after a certain interval and write a cron job to check the interval and switch OFF the flag in DB, is there any better way to do this, please guide?
Create your own session driver handler in laravel and execute the code you want in "destroy" and "gc" events of your handler.
You can simple copy the default DatabaseSessionHandler code from
Illuminate/Session/DatabaseSessionHandler.php
and name it lets say CustomeSessionHandler.php.
make sure to register the new handler in AppServiceProvider
public function boot()
{
Session::extend('custom', function ($app) {
$connection = $app['config']['session.connection'];
$table = $app['config']['session.table'];
$lifetime = $app['config']['session.lifetime'];
$connection = $app['db']->connection($connection);
return new CustomSessionHandler($connection, $table, $lifetime, $app);
});
}
and in your .env or your config/session.php
use the new driver
SESSION_DRIVER=custom

force a specific user to logout in codeigniter with destroying his session

I want to force a user to logout when I change it's status to 'inactive' using session in codeigniter.
I used the method below, but it destroyed my own session:
function deactivate($user_Id)
{
$this->session->unset_userdata(array('user_Id' => $user_Id));
}
I happened to need this feature implemented and here is how I did it:
record user session id as last_active_session in db after login
find that session id and delete it from session table when this user is banned or anything.
You can also use this to prevent concurrent login such as the last successful login user bump the previous one.
Use the sess_destroy() method instead:
function deactivate() {
$this->session->sess_destroy();
}

Yii setFlash with Logout

Im tying to use the command sequence:
Yii::app()->user->setFlash('success', "Successful!");
Yii::app()->user->logout();
$this->redirect(array('user/login'));
The user got logged out and redirected, but the Setflash does not work.
I also tried to change the order of 2 frist commands, but got the same problem.
If I do not logout the user, the Setflash works fine.
How can I make both commands work?
this should work
Yii::app()->user->logout();
Yii::app()->session->open();
Yii::app()->user->setFlash(...);
If you need to destroy a whole session but you want to set a flash afterwards, you may extends CWebUser this way:
<?php
class BaseWebUser extends CWebUser
{
public function logout($destroySession = true)
{
parent::logout($destroySession);
Yii::app()->session->open();
}
}
?>
have a closer look here
I think you can use this :
public function afterLogout() {
// Create new session
$session=new CHttpSession;
$session->open();
// Set flash message
Yii::app()->user->setFlash('success', 'You are logged out successfully.');
// Prepare target URL after logout
$continue_url = Yii::app()->request->hostInfo . Yii::app()->createUrl('');
// Redirect
CController::redirect($continue_url);
}
Put it inside your WebUser components.
Flash messages are stored in the session. Logging the user our destroys the user's current session. Once session_destroy() is called, you must call session_start() again in order to generate a new session ID and have this work. Yii most likely does not do that.
If it's that important that you have a "Successful" message indicating that the logout worked - then redirect the user to a "logout successful" page. Alternatively, you can look into overriding the way Yii performs a logout - although I wouldn't recommend it.

How to programmatically recreate php yii session?

From my application view I need to programmatically logout current user and login another one right after that.
I want to login the second user into his own different CHttpSession (with another sessionID and so on). I need it for a security reasons.
How to implement this in Yii framework ?
Code below
$oSession->destroy();
$oSession->open();
doesn't work as expected..
looks like you are trying to impersonate users:
Create a function in your UserIdentity that would alow you to login as another known user:
protected function logInUser($user)
{
if($user)
{
$this->_user = $user;
$this->_id=$this->_user->id;
$this->setState('name', $this->_user->name);
$this->errorCode=self::ERROR_NONE;
}
}
In your controller, call this function to get the UserIdentity object and then use the Yii's CWebUser login
$ui = null;
$user = User::model()->findByPk($userId);
if($user)
{
$ui = new UserIdentity($user->email, "");
$ui->logInUser($user);
}
Yii::app()->user->login($ui, 0);
Remember to protect this controller's action from non authorized users.
A possible tricky way (tested):
session_unset();
Yii::app()->user->id = $the_new_id;
When the above code is executed, nothing visible happens on the page so you may want to redirect the browser:
$this->redirect('somewhere');
Upon the next page load, the user with the $the_new_id will be logged in

Categories