I am working on a project which is built using CakePHP 2.8 . At the time of login I am setting a FLAG to 1 and on logout it is set to 0, so that account can be logged in on single computer at a time. It is working great till this part.
The problem I am facing is at the SESSION TIMEOUT. I am confused that how to set the flag to 0 in database when session timeout. Is there any way to run an update query on session timeout.
I am using the CORE config file to set the SESSION timeout limit as follow:
Configure::write('Session', array(
'defaults' => 'php',
'timeout' => 30, // The session will timeout after 30 minutes of inactivity
'cookieTimeout' => 1440, // The session cookie will live for at most 24 hours, this does not effect session timeouts
'checkAgent' => false,
'autoRegenerate' => true, // causes the session expiration time to reset on each page load
));
And this is my logout function
public function logout() {
$id = $this->Auth->User('id');
$this->User->updateAll(array('WebLoggedIn'=>0), array('User.id'=>$id));
$this->Auth->logout();
// redirect to the home
return $this->redirect('/');
}
That won't work
The idea in the question isn't going to work. The moving parts related to sessions, with the config in the question are:
A file stored on the server with the serialized session data in it, which updates every time the session is written to
A standard php cron job, which deletes session files that have expired (see /etc/cron.d/php5 or equivalent)
A browser cookie, which links the user's browser session to the file on the server
When a user's session time's out - that either means the session id they presented does not correspond to a file on the server, or they simply didn't present a session cookie. There's no "hey this session expired" event at the moment it expires, and there's no guarantee a user would provide the old session id for you to check if it's valid.
Working proposal
A simple (and this also means naïve and possibly easy to bypass) solution is to not store a boolean, and instead store the time their session will expire in the db. I.e. have code similar to this:
// App Controller
public function beforeFilter()
{
$userId = $this->Auth->user('id');
if ($userId) {
$this->User->updateAll(
array('active_session_expires'=> time() + (30 * 60)),
array('User.id'=>$id)
);
}
}
And in the users controller:
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
if ($this->Auth->user('active_session_expires') > time()) {
$this->Flash->error('You are still logged in somewhere else');
return $this->logout();
}
$this->User->updateAll(
array('active_session_expires'=> time() + (30 * 60)),
array('User.id'=> $this->Auth->user('id'))
);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
public function logout()
{
$id = $this->Auth->User('id');
if ($id) {
$this->User->updateAll(
array('active_session_expires'=> time()),
array('User.id'=>$id)
);
$this->Auth->logout();
}
return $this->redirect('/');
}
I.e. every time they do something - update the db to keep track of their activity. If they try to login before their existing session expires - immediately log them out. Expect to need to test/modify this example code, it is provided to give you an idea, not necessarily a complete and working solution.
This is similar to an answer you've already been linked to.
Related
I am tracking the login history of a user in my website, and for that I need to display if there is any other session open in another browser or device. Plus, I need to provide a functionality wherein a user can logout a session that is open in a different browser/device from the current session. I am using redis-server. The things which I have done so far are as follows.
In the .env file, I have updated the CACHE_DRIVER and SESSION_DRIVER to redis.
In session.php and cache.php, I have set driver to redis.
Next, at the time of login, I connect to the redis server and store the session id for a specific user.
$redis = Redis::connection();
$redis->sadd('user:sessions:'. Auth::user()->id, $session_id);
Once this is done, I can see the multiple session id's per user using redis-cli command.
> SMEMBERS user:sessions:1
> "95008658737a7f937c614bccbb748443a649c515"
> "f1f14db9b1760254a4072fe9b440f9acbacc8974"
> GET laravel:95008658737a7f937c614bccbb748443a649c515
> "s:332:\"a:5:{s:6:\"_token\";s:40:\"HAuA200irzF63fgFw4vCVkrsZNuqTk6o2XLkHzlu\";s:9:\"_previous\";a:1:{s:3:\"url\";s:33:\"http://localhost:8000/security\";}s:5:\"flash\";a:2:{s:3:\"old\";a:0:{}s:3:\"new\";a:0:{}}s:50:\"login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d\";i:1;s:9:\"_sf2_meta\";a:3:{s:1:\"u\";i:1464957596;s:1:\"c\";i:1464957416;s:1:\"l\";s:1:\"0\";}}\";"
This output makes me assume that the redis option for storing sessions in laravel is working fine.
Next, for logging out a user from a specific session, I am using the following set of instructions:
public function logoutSession($session_id) {
$redis = Redis::connection();
$redis->del('laravel:' . $session_id);
$redis->srem('user:sessions:' . Auth::user()->id, $session_id);
}
Once this function is called, the corresponding session id is successfully deleted.
Running the SMEMBERS and GET commands again in redis-cli proves it.
So the scenario is, I try to delete a session opened in say, Firefox from another browser say, Chrome. Cliking on the logout link (for Firefox session from Chrome session) deletes the Firefox session from redis-server successfully, but if I try to refresh the Firefox browser, I am still logged in.
Just an image displaying the functionality I am trying to achieve
I cannot understand where I am going wrong exactly. Any sort of help will be highly appreciated.
Thanks
NOTE: I am already using the 'auth' middleware, so that cannot be the problem. Plus, if I try to debug the code, I am seeing different session_ids in $_COOKIE variable and http request variable. Is this normal?
I had a similar issue once myself. I was frustrating myself with cache invalidation etc... and then after going through the "log in" process again I had forgotten to check for the "remember me" cookie.
https://laravel.com/docs/master/authentication#remembering-users
Turns out I had cleared the Cache successfully, but needed to call:
Auth::logout();
In order to remove the "remember_token" from the database. Otherwise, Laravel sees this as a valid way to reboot the Auth system, logging the user back in and setting a new session cookie.
Using your code:
public function logoutSession($session_id) {
$redis = Redis::connection();
$redis->del('laravel:' . $session_id);
$redis->srem('user:sessions:' . Auth::user()->id, $session_id);
Auth::logout();
}
But this logs out from ALL sessions, including the one you're working with.
For your problem, I propose using the Auth::viaRemember() method to determine if the client creating the request along with the cookie is the one you're currently operating with. It differentiates between your current session cookie and a "remember me" cookie:
if (Auth::viaRemember())
{
// Check Redis for session info
// Do something if not found, otherwise just proceed
}
**EDIT**: I found this after posting my answer: Laravel Auth::logout not removing remember me cookie
So to recap:
if (Auth::viaRemember())
{
if (!Redis::get($key))
{
// Get remember_me cookie name
$rememberMeCookie = Auth::getRecallerName();
// Tell Laravel to forget this cookie
$cookie = Cookie::forget($rememberMeCookie);
return Redirect::to('/')->withCookie($cookie);
}
}
I am starting the session as
session_start(); //So This session will get destroyed when user exit the browser
I set the cookie as
setcookie('cookie',$value,time()+3*60);
$_SESSION['cookie'] = true; //When user log in, I don't need to get the
salt again from db and do all the security
checks. Assume it as a cache
This suggesion is suggested in this question.
The problem is, cookie gets expired after 3 minutes. But session exists. So user tries to login after 3 minutes
if($_SESSION['cookie'])
{
//success
}
else
{
//Authenticate user
}
But this is wrong. Since cookie expired, else part should execute. But if part gets executed. I understand that session is still alive. How do I handle this situation?
I understand that it is due to that time()+3*60. But how do I implement that cache thing?
You need check cookie not in $_SESSION.
Check them in $_COOKIE.
As example:
if($_COOKIE['cookie'])
{
//success
}
else
{
//Authenticate user
}
Cookies and session variables are not related (except that PHP internally uses a cookie named PHPSESSID to connect the client to its session). Expiring the cookie doesn't have any effect on a session variable with the same name. If you want to test whether the cookie has expired, use:
if (isset($_COOKIE['cookie']))
I am trying to log a user out of my CMS after a set amount of time. By inactive I mean has not clicked the mouse or typed on there keyboard. So after 30 minutes of inactivity my log out function is ran.
There is already a log out function built in to the CMS I am using -
<?php
session_start();
if (isset($_SESSION['user_id'])){
$login = 1;
}else{
$login = 0;
}
function confirm_logged_in() {
if (!isset($_SESSION['user_id'])) {
//redirect
header("Location: /_cms/login.php?login=0");
}
}
function logout(){
$_SESSION = array();
if(isset($_COOKIE[session_name()])){
setcookie(session_name(), '', time()-4200, '/');
}
session_destroy();
}
?>
Someone else wrote this code and it works. However I don't know the exact time it takes to log out an inactive user. The preset time is - 4200. What I want to find out is how long that takes to logout and if I can change it to any time I want. Can anyone advise?
The -4200 is just to destroy the cookie. Cookies are destroyed by setting a time in the past for them. So setting 4200 seconds backwards is just as effective as 1 second backwards.
To logout users there are multiple methods. You can have a your own cookie set with the last active time (set the time every time the user visits a page). At the beginning of each script include a function which gets this cookie and checks the value which should contain the last active time. If this time is older than your allowed inactive time, then destroy this cookie and destroy your session as well, if not, then update the value to the current time.
Of course, you can also store inside the session itself the last active time, which is a much more efficient way removing the overhead of cookie transfer and management.
EDIT
Below is a minimal code to check for the last active time and logout the user:
function login(){
//check login username/pass etc...
$_SESSION['last_active_time'] = time();
}
function auth(){
if($_SESSION['last_active_time'] < (time() - 1800)){ //1800 is 30 minutes (time in seconds)
logout(); //destroy the session in the logout function
}
else{
$_SESSION['last_active_time'] = time();
}
//do some auth related things
}
That's the basic logic behind this. Of course you would need to implement other stuff you need along with security, checking, etc....
I will try to answer your question and have some questions too.
What CMS are you using? If you can name the CMS, we can provide detailed and accurate solution
Regarding your function logout() and about the setcookie and -4200, whenever you call the function logout, it is checking if there is any coockie set. If yes, then it is just setting the EXPIRY TIME to 4200 seconds ago ie 7 minutes ago from current time. ie. It invalidates the Coockie which is present at present.
Refer the link: http://php.net/manual/en/function.setcookie.php
Now, what you want is that after 30 mins of inactivity, user should be logged out. Your current code is not built for that. You should write the logic to keep checking the last active time and should invoke the logout function if it is more than 30 mins. Now the question is, how to do? Am just modifying your code a bit
if (isset($_SESSION['user_id'])){
$login = 1;
// If the user has performed action within 30 minutes
if($_SESSION['last_active_on'] > (time() - (30*60))){
$_SESSION['last_active_on'] = time(); // Re-set the current time as Last Active
}else{
// User has done some action after 30 minutes.
logout(); // Invoke the Logout functionality
}
}else{
$login = 0;
}
Remember: time() Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
You have not added your login function here. You should modify your login function and should add one more line in that as
$_SESSION['last_active_on'] = time();
One more thing. All this can happen only if the requests are sent to the server. For example: Assume at 4:00 PM, due to some action, there was a server call. Assume at 4:25 you are moving your mouse cursor or clicking anywhere on the page, but if it doesn't send any request to server, then it is considered as in-active itself. And at 4:35 PM if the user does something where the request is sent to server [Normal request or Ajax],
then as per the server, it is 35 mins inactive state, hence it will logout. Hope this answers your question.
You can even refer the SO question: User Inactivity Logout PHP It may also help you.
I'm new to laravel and added a userauthentification to my application. Now I don't want authenticated users to be remembered after closing the browser, but this is actually the case. How do I stop this? The remember_token is a nullable varchar(100), an it is set with the first login and then updated everytime the user logs out.
Shouldn't users only be remembered when i pass true as parameter in the auth::attempt()?
Tyvm in advance...
This is part of my model:
$validation = Validator::make($input, $rules, $messages);
if ($validation->fails())
{
$this->response['message'] = $validation->getMessageBag()->first();
}
else
{
$this->response['message'] = $messages['user.missing'];
if (Auth::attempt(array(
'emailadress' => $input['emailadress'],
'password' => $input['password'])))
{
$this->response['valid'] = true;
$this->response['message'] = 'Hey '.Auth::user()->firstname.'!';
$this->response['redirect'] = '/hello';
}
}
return $this->response;
As you said, you are not including the remember parameter in Auth::attempt(), so indeed - your users are not being remembered.
They will be logged in for as long as their session remains active. Browsers don't empty their cookies upon closing unless the user specifically tells it to, and there is certainly no way for you to "detect" their browser closing from the server-side and expire their session from that end.
So, you must either choose a different session lifetime in your PHP settings if you want sessions to expire more quickly, or you can do something like this where you timestamp their activities and log them out if they've been idle for a set time.
If you choose to shorten your session lifetimes, this can be accomplished in php.ini:
ini_set(‘session.gc_maxlifetime’,30);
ini_set(‘session.gc_probability’,1);
ini_set(‘session.gc_divisor’,1);
Background:
I was Using laravel 4.0.x , and resetting the remember_me cookie expiry date to 1 month (since the default is 5 years ) using this code :
App::after(function($request, $response)
{
// If the user is tryin to log_in and he wants to stay logged in, reset the remember_me cookie expiration date from 5 years to 1month
$remember = Input::get('remember',false);
if ($remember) {
if ( Auth::check()){ // check if the user is logged in
$ckname = Auth::getRecallerName(); //Get the name of the cookie, where remember me expiration time is stored
$ckval = Cookie::get($ckname); //Get the value of the cookie
return $response->withCookie(Cookie::make($ckname,$ckval,43200)); //change the expiration time to 1 month = 43200 min
}
}
That code is from app\filters.php of course, and it was working like charm.
The problem :
I recently updated laravel from 4.0.x to v4.1.28, and now the remember_me cookies are set to 5 years, I tried for the last hours digging in the code trying to debug but with no luck :( .
Notice that changing $ckname to another value like "test" in the last line of the above code works just fine, it creates a "test" cookie with an expiry of 1 month like I intended
return $response->withCookie(Cookie::make("test",$ckval,43200));
I don't really understand why the remember_me cookie persist to the 5 years expiry date !
Any help would be appreciated :)
Abdou.
Update:
The question is not why I want to change the cookie expiry date, but it's why the cookie won't get updated?! . Thanks.
This is intentional behavior with Laravel.
The remember_me settings is to "permanently" remember the User, until such time as they 'logout' of the application.
If you dig into the Auth classes - it specifically says it is a "permanent cookie".
public function login(UserInterface $user, $remember = false)
{
$this->updateSession($user->getAuthIdentifier());
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember)
{
$this->createRememberTokenIfDoesntExist($user);
$this->queueRecallerCookie($user);
}
There is no way to set the cookie to anything other than 'permanent' (aka 5 years).
Also - the Laravel docs state the remember me is forever:
If you would like to provide "remember me" functionality in your application, you may pass true as the second argument to the attempt method, which will keep the user authenticated indefinitely (or until they manually logout)
Edit: as you've updated your question - I looked into it more. This works for me:
public function login()
{
if (Auth::attempt(array('email' => Input::get('email'), 'password' => ), true))
{
$ckname = Auth::getRecallerName();
Cookie::queue($ckname, Cookie::get($ckname), 43200);
return View::make('welcome');
}
}
It only sets the 'remember_me' cookie to 1 month.