Am creating a web application with the codeigniter framework, am working with version 2.0.3.
My makes ajax requests to update the page from time to time, and fetch notifications.
I've visited the codeigniter forums and asking questions about codeigniter sessions and ajax and found this snippet of code which i used, and saved in libraries and class "My_session.php"
class MY_Session extends CI_Session {
/**
* Update an existing session
*
* #access public
* #return void
*/
function sess_update() {
// skip the session update if this is an AJAX call! This is a bug in CI; see:
// https://github.com/EllisLab/CodeIgniter/issues/154
// http://codeigniter.com/forums/viewthread/102456/P15
if ( !($this->CI->input->is_ajax_request()) ) {
parent::sess_update();
}
}
}
But ever since i added this code i am unable to stay logged for more than five minutes without being logged out, or sometimes not being able to login in at all.
Does anyone have a similar experience?
If you want to stay logged in longer you have set sess_expiration for more then five minutes in your application/config/config.php
what happens when you evaluate the is_ajax_request()? in that code snippet add:
echo 'AJAX: ' . $this->CI->input->is_ajax_request(); exit;
to see if the if statement is working correctly. it might be returning false and updating the session each time. just a quick place to start :)
Related
I'm creating 2 projects in 2 diferent domains domain1.tld and domain2.tld.
The domain1.tld is the main event producer page and the domain2.tld is one of its events. I want to share the same sessions (they actually share the same database and the same apache server). I tried to change the session driver to "database" and create a session table, but nothing happens, if i'm log in domain1.tld nothing happens in domain2.tld.
I really have searched in the net but i have found nothing
you can't do this in your way...
when you set session, a cookie set in browser for track stored session in server side.
if you want to share session between two domain you should share cookie between to site bot you can not do it (you can do it just in sub domains of ONE domain)
but there is a little hack :
The easiest work-around is to pass login/credential information from website A to website B and have website B set a seperate cookie. For example, after logging into website A you could have them quickly redirected to website B with an encrypted querystring. Website B could then read the information, set its own cookie, and redirect the user back to site A.
It's messy but possible.
Step 1: Set Session Driver for Shared Session Data
First, set your session driver to a database or cache that is shared across both domains. Your session driver cannot be file
Step 2: Implement Cross-Domain Session IDs
Session ids are passed around by cookies in Laravel. Since your websites are on different domains the session cookie does not transfer over. One way to solve this is to append them to the query string of all your requests like so: domain2.tld/?session_token=abcds2342
Within your code there must be some login that detects a session and then query the database/cache (your session driver) for a result. If a result is found, you set the session ID manually and start the session:
session_id('abcds2342');
session_start();
Be careful to check both the IP address and the session ID
to prevent people from guessing someone elses SessionID and thus
logging in as another person
Step 2A: To do this you can implement a custom middleware that overrides StartSession. This middleware should override getSession and before it checks for session_id in cookie, check if we have a token present in the Request. Sample code below:
<?php
namespace App\Http\Middleware;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Http\Request;
use App\SessionShare;
use Closure;
class StartSessionWithSharer extends StartSession
{
/**
* Get the session implementation from the manager.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Session\SessionInterface
*/
public function getSession(Request $request)
{
$session = $this->manager->driver();
/**
* Check if we can find a valid session token from saved records
*/
if($request->get('session_token') && !empty($request->get('session_token'))) {
$sessionShare = SessionShare::valid()->whereToken($request->get('session_token'))->first();
if($sessionShare)
$session_id = $sessionShare->session_id;
}
/**
* Fallback to session in browser
*/
if(!isset($session_id) || !$session_id)
$session_id = $request->cookies->get($session->getName());
$session->setId($session_id);
return $session;
}
}
Step 2B: Then create a custom service provider to override SessionServiceProvider like so:
<?php namespace App\Providers;
class CustomSessionServiceProvider extends \Illuminate\Session\SessionServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerSessionManager();
$this->registerSessionDriver();
$this->app->singleton('App\Http\Middleware\StartSessionWithSharer');
}
}
And then remove the old SessionServiceProvider from config/app.php and instead use above.
Step 2C: Then create your App\SessionShare model for the table to store session IDs. Also, the above code doesn't take care of checking IP address so you would have to add that in to make it secure and prevent brute force attacks
Step 2D: Oh and finally don't forget to append the get parameter for session_token for all your requests
Note that the above implementation is for a database session driver. Of course, you can do this for a cache driver too. The only thing that would change is the model implementation (step 2C) to validate the session
I've written a magento controller which stores some filter information inside the customer session.
According to the magento 2 documentation I use dependency injection to let magento generate the session object for me:
/**
* #var \Magento\Catalog\Model\Session
*/
protected $_filterSession;
/**
* #param \Magento\Customer\Model\Session $filterSession
*/
public function __construct( \Magento\Customer\Model\Session $filterSession)
{
$this->_filterSession = $filterSession;
}
The injection process is working quite well. I'm able to access the session, store a variable in it and return it on a further invocation.
But magento seems to discard the whole session information from time to time. I cannot exactly identify the moment magento discards the information, it seems kind of random.
Here is the code:
$this->_filterSession->setFrequency($frequency);
$frequency = $this->_filterSession->getFrequency();
I tried out different session scopes but the behaviour is the same.
After many attempts I tried to use the PHP-session to store the information, but even this session was discarded from time to time.
I don't know what I'm doing wrong or what could be the reason for this weird behaviour. Does anybody else have a similar problem or an idea whats the reason?
Thanks in advance,
Thomas
This usually happens when the browser loses session cookies. You should check if domain name changes during the session when the variables are lost. Or with a different browser. Might be some misbehaving browser plugin. Or some Magento extension. Many Mageno 2 extensions currently are poorly written.
I had a similar problem using PHP. I had set session.referer_check. So, when a user was coming from an external page, the session was lost. If this is your problem, simply ini_set('session.referer_check', '');.
I didn't find a solution to the problem itself, but avoided it.
For those who also encounter the problem, here is my bandaid fix:
I introduced a new cookie
public function getFrequency()
{
$frequency = $this->_cookieManager->getCookie(self::FREQUENCY_SESSION_KEY);
if( !isset( $frequency ) )
{
$frequency = self::FREQUENCY_DEFAULT_VALUE;
}
return $frequency;
}
public function setFrequency( $frequency )
{
$metadata = $this->_cookieMetadataFactory
->createPublicCookieMetadata()
->setPath($this->_sessionManager->getCookiePath())
->setDomain($this->_sessionManager->getCookieDomain());
$this->_cookieManager->setPublicCookie(
self::FREQUENCY_SESSION_KEY,
$frequency,
$metadata
);
}
for further details I recommend you to look at this thread.
Regards, Thomas
Is there a way to check if a user already has a valid session on a different machine?
What I want to do is when a user logs in, destroy an other sessions which they may already have, so that if they forget to logout from a computer say on campus or at work, and then they log in at home, it will destroy those other 2 sessions so they are no longer logged in?
Facebook employs this in some way.
My only thoughts so far is something to this effect:
$user = User::find(1); // find the user
Auth::login($user); // log them in
Auth::logout(); // log them out hoping that it will destroy all their sessions on all machines
Auth::login($user); // log them in again so they have a valid session on this machine
I have not had the chance to test this, and I do not know if Auth::login($user); will destroy all sessions for that user, or only the current one.
Thanks!
You can save a session_id within a user model, so that:
When logout event is fired (auth.logout) you would clear it.
When new logging event is fired you can check if attribute session_id is not null within the user model.
If it's not - destroy previous session by:
Session::getHandler()->destroy($user->session_id);
$user->session_id = Session::getId();
Hope that would help!
I realise this is an old question, but there is now a method in laravel 5.6 that does exactly this, so it may be useful for someone coming to this later. You can also retro-fit this method to earlier versions of laravel very easily.
See the docs at https://laravel.com/docs/5.6/authentication#invalidating-sessions-on-other-devices
I had the same use case as you (log out all other devices on log-in). I overrode the default login method to add my own custom logic (first copying the default login method from vendor/laravel/framework/src/illuminate/Foundation/Auth/AuthenticatesUsers.php)
In that method, there is the line if ($this->attemptLogin($request)) - within this, before the return statement, add your call to logoutOtherDevices, as below
if ($this->attemptLogin($request)) {
//log out all other sessions
Auth::logoutOtherDevices($request->password); //add this line
return $this->sendLoginResponse($request);
}
Also ensure you have un-commented the Illuminate\Session\Middleware\AuthenticateSession middleware in your app/Http/Kernel.php, as per the docs
(note that I haven't tested the above code as I was using an older version of laravel that doesn't have this method, see below). This should work in 5.6 though.
Older Laravel versions
I was actually using laravel 5.5, so didn't have access to this handy method. Luckily, it's easy to add.
I opened a laravel 5.6 project and copied the logoutOtherDevices method from vendor/laravel/framework/src/illuminate/Auth/SessionGuard.php - for reference I have pasted below
/**
* Invalidate other sessions for the current user.
*
* The application must be using the AuthenticateSession middleware.
*
* #param string $password
* #param string $attribute
* #return null|bool
*/
public function logoutOtherDevices($password, $attribute = 'password')
{
if (! $this->user()) {
return;
}
return tap($this->user()->forceFill([
$attribute => Hash::make($password),
]))->save();
}
I then copied this into my LoginController - it could go somewhere else of your choice, but I've put it here for ease / laziness. I had to modify it slightly, as below ($this->user() becomes Auth::user())
/**
* Invalidate other sessions for the current user.
* Method from laravel 5.6 copied to here
*
* The application must be using the AuthenticateSession middleware.
*
* #param string $password
* #param string $attribute
* #return null|bool
*/
public function logoutOtherDevices($password, $attribute = 'password')
{
if (! Auth::user()) {
return;
}
return tap(Auth::user()->forceFill([
$attribute => Hash::make($password),
]))->save();
}
I can then call this method in my login method, as specified earlier in my answer, with a slight adjustment - $this->logoutOtherDevices($request->password);
If you want to test this locally, it seems to work if you open your site on a normal and an incognito window. When you log in on one, you'll be logged out on the other - though you'll have to refresh to see anything change.
I hope you will see this job:
Session::regenerate(true);
a new session_id be obtained.
This may not be the best answer, but first thing that came to my mind was lowering the timeout on the session.
In app->config->session.php there's a setting for both lifetime and expire_on_close (browser).
I'd try looking into that for now, and see if someone else comes up with something better.
Using Laravel 4, I am trying to run some selenium tests but want to auto login users. I have the following but it seems the session ID in the browser differs to that which I can get through the test.
$this->_user = User::create([
...
]);
Auth::login($this->_user);
... or ...
$this->app['auth']->login($this->_user);
Neither work (even with $this->startSession() ).
I have also tried getting the session id from redis and preceeding the two above calls with $this->app['session']->setId($id);
What's the correct way of modifying the session that the browser has?
Edit: I think the problem comes from Auth\Guard::getName generating a unique id
If you want to use Selenium to fill in and submit a login form, you don't need to worry about the unique session ID. Selenium is able to login to the website just like a normal user.
However, if you want to bypass the login page and directly login to the website, you may need to save the login authentication cookies and then re-use it in all your test scripts. You can refer to this example. It is in Python though.
Simply use Auth::loginAsId() within a controller.
On Laravel 4.2, I extended Laracasts\Integrated into Selenium2 and then extended my tests from there. tests/Selenium2.php looked something like this:
use Laracasts\Integrated\Extensions\Selenium;
use Laracasts\Integrated\Services\Laravel\Application as Laravel;
class Selenium2 extends Laracasts\Integrated\Extensions\Selenium {
use Laravel;
protected $baseUrl = 'https://www.myapp.loc';
/**
* Go to an arbitrary url
*
* #param $url
* #return $this
*/
protected function goToUrl($url)
{
$url = $this->baseUrl() . $url;
$this->currentPage = $url;
$this->session->open($url);
return $this;
}
}
Notice that it uses $this->session, which is an instance of the built-in webdriver, which you'll need to use in order to keep the session.
For Laravel 5.3, Something like this:
Route::get('loginasid/{id}', function($id) {
if ( App::environment('testing')) {
Auth::loginUsingId($id);
}
}
then in your test
$this->visit( 'loginasid/' . $user->id );
$this->visit( 'otherpage' );
This is tested on Laravel 5.3. Your question asks for 4.x so I included answers for both, but you're better off upgrading to 5.3, the first step of which is to write your tests ;)
I have an application built in CakePHP that is doing file uploads. For each file upload, I know the user's id, so I grab it from $this->Auth->user()
Now, what I have found is that when I am on the page, I will upload files while logged in but sometimes when I print_r the $this->Auth->user() it returns nothing back, and then the next time I try a file upload it will come back, all without e logging in or out. It seems extremely inconsistent, in that sometimes it is in there but other times it doesn't see it.
What am I missing? Thanks!
Why don't set a var in your app_controller to $this->Auth->userModel so it's accessible by the rest of the application.
In my app_controller I call the below in before_filter to set $current_user.
/**
* Sets a value for current user $current_user.
* #return boolean
*/
function __setCurrentUser() {
$user = null;
if ($user = $this->Auth->user()) {
$this->set('current_user', $user[$this->Auth->userModel]);
return true;
} else {
return false;
}
}
Elsewhere in my app, I can access $current_user's id via $current_user['id'].
You can also always grab userinfo from the Session that is created by AuthComponent. If $this->Auth->user('id') is empty.. I'm wondering if there is actually a valid session! Are you sure you haven't been logged out in the meantime and the page isn't requiring a login for some reason?