Welcome Guest, Not a member yet? Register   Sign In
Code Igniter keeps losing my session ID
#1

I've posted this question on Stack Overflow - I'm going to cover my bases and post it here as well.

In my attempts to store session information in Code Igniter I am running into a pretty frustrating problem. Disclaimer - I am still quite green with both PHP and CI so please be kind...

Another source of assistance has given me This code to test and it works fine with the following configuration -

Code:
   $config['sess_driver'] = 'database';
   $config['sess_cookie_name'] = 'QATime';
   $config['sess_expiration'] = 7200;
   $config['sess_save_path'] = 'ci_sessions';
   $config['sess_match_ip'] = FALSE;
   $config['sess_time_to_update'] = 300;
   $config['sess_regenerate_destroy'] = FALSE;
   .
   .
   .
   $config['cookie_prefix']    = '';
   $config['cookie_domain']    = '';
   $config['cookie_path']        = '/';
   $config['cookie_secure']    = FALSE;
   $config['cookie_httponly']     = FALSE;

Please note - this works fine. The database stores the values in the simple test and they are loaded again when the "web page" is reloaded. When I say that this 'works', what I mean is that when I run it, I get the following -

[Image: Qe5Qv.png]

When I terminate and relaunch I get the following -

[Image: ex1Vm.png]

To me this says that the session data and database information is working as it should be, and there was much rejoicing.

What does go wrong is when I attempt to log in. The session information is... 'lost' or something somehow and I can't figure out why that is happening.

This is the chain of execution as best as I can trace it -

Within the `routes.php` I have the following -

Code:
$route['default_controller'] = 'Main/view';

Within the `Main` class `view` method I have the following -

Code:
$this->load->model('User_Model', 'User');

The User_Model class definition and constructor -

Code:
CLASS User_Model EXTENDS MY_Model{
    public FUNCTION __construct(){
        PARENT::__construct();
        $this->database = $this->load->database('users', TRUE);
        $this->table = 'users';
        $this->idKey = 'UserID';
    }
    .
    .
        .
}

MY_Model definition and constructor -

Code:
CLASS MY_Model EXTENDS CI_Model{
    
    public $ID;
    protected $database, $table, $idKey, $row;
    .
    .
    .
    public FUNCTION __construct(){
        PARENT::__construct();
    }
    .
    .
    .
}

The next few lines within the `Main` class `view` method -

Code:
$this->load->library('Form_validation');
$this->form_validation->set_error_delimiters('<div class="error">', '</div>');
$this->form_validation->set_rules('username', 'Username', 'trim|required|min_length[3]|max_length[20]');
$this->form_validation->set_rules('password', 'Password', 'required|min_length[3]|max_length[20]');
        
IF ($this->form_validation->run()){
    IF (!$this->User->login(
        addslashes(strtolower($this->input->post('username', TRUE))),
        addslashes($this->input->post('password', TRUE)),
            $this->getIP())){
                /*This block is executed fine and is not relevant to this question.*/
            } ELSE {
                /*We will come back to this after expounding the Main getIP() and User login(...) functions*/
            }

Main getIP Function -

Code:
private function getIP(){
    RETURN !EMPTY($_SERVER['HTTP_CLIENT_IP'])
        ? $_SERVER['HTTP_CLIENT_IP']            
        : !EMPTY($_SERVER['HTTP_CLIENT_IP'])
            ? $_SERVER['HTTP_CLIENT_IP']
            : $_SERVER['REMOTE_ADDR'];
}

User_Model login function -

Code:
public FUNCTION login($userName, $password, $IP){
    $row = $this->database->get_where('users', ARRAY('UserName' => $userName))->row_array()['UserName'];
    IF (!ISSET($row)){
        /*The row is set fine - this isn't relevant to the question.*/
    } ELSE {
        $row = $this->database->get_where('users', ARRAY('UserName' => $userName, 'Password' => $password)
            )->first_row('array');
        IF (!ISSET($row)){
            /*Not relevant to the question.*/
        } ELSEIF(FALSE /*Boolean function call not relevant to the question.*/){
            /*Not relevant to the question.*/
        } ELSE {
            $this->ID = $row['UserID'];
            $this->database->insert(
                'user_history',
                ARRAY('UserID' => $this->ID, 'LoginDate' => date('Y-m-d H:i:s'), 'IP' => $IP));
            /*$this->session->set_userdata($row)
            This I tried in the past but the Model evidently does not have
            implicit access to session data. I had attempted to circumvent
            this in the MY_Model implementation but decided that it would be
            better to handle this in the controller.*/
            RETURN TRUE;
        }
    }
}

Now we return to the `Main` `view` method execution where we left off -

Code:
} ELSE {
    $this->session->set_userdata($this->User->staleRow());
    SWITCH($this->User->stale('Permissions')){
        CASE 'ADMIN':
            redirect('Admin');
            BREAK;
        /*Non question relevant code omitted*/
    }
}

The User staleRow() function is actually inherited from the `MY_Model` class -

Code:
/**
* Returns an instantiated row of stale data.
*/
public FUNCTION staleRow(){
    if (!ISSET($this->row))
        $this->row = $this->freshRow();
    return $this->row;
}

/**
* Returns a row of fresh data.
*/
public FUNCTION freshRow(){
    return $this->database->
        select()->
        from($this->table)->
        where("$this->idKey = $this->ID")->
        get()->first_row('array');
}

And then we have the Admin class definition and constructor -

Code:
CLASS Admin EXTENDS CI_Controller{
    
    public FUNCTION __construct(){
        PARENT::__construct();
        $this->output->enable_profiler(TRUE);
        $this->load->model('User_Model', 'User');
        $this->User->ID = $this->session->UserID;
        IF (!$this->session->UserID){
            $this->session->set_flashdata('user_msg', 'Login Required');
            redirect(base_url(), 'refresh');
            EXIT;
        } ELSEIF ($this->session->Permissions != 'ADMIN'){
            $this->session->set_flashdata('user_msg', 'Admin Login Required');
            redirect(base_url(), 'refresh');
            EXIT;
        }
    }

The Admin class is wherein the problem lies - for reasons beyond what my limited experience permits me to comprehend, when it reaches this point, the session data is not present - Please see the following pictoral evidence -

[Image: 6A9Hu.png]

This is the session variable within the `Main` class after the line `$this->session->set_userdata($this->User->staleRow());` is called.

This is the session variable within the `Admin` class after the `PARENT::__construct();` method is called -

[Image: OCw0m.png]

I'm sure it's something I'm not doing correctly. Why is the session data not being loaded into the Admin class controller?


EDIT 1
------

Please know that I am running Code Igniter version 3.0.1

Below it was suggested I check the Cookie ID in a couple of places - this was the result:

After the 'login' button was clicked -
[Image: CNVTO.png]

From this I can see the Cookie ID within the array (QATime = some obscenely long integer value))

After the `redirect('Admin');` call, this value is no longer present -

[Image: kYhmR.png]

I understand the function of this ID is to help find session data, so it makes perfect sense to me that the session data is lost because this ID is not being kept. So why is the Cookie ID being lost?
Reply
#2

Ignoring the oddities of your coding style and the insecurity of your ability to perform a get_where() with the user's password, the way in which you trace your chain of execution is enlightening, even if confusing. If you don't have access to $this->session in your model, the first thing that comes to mind is where are you loading the session library?

Also, though probably not relevant, is there a reason you've defined a getIp() method instead of using $this->input->ip_address()?
Reply
#3

(This post was last modified: 09-16-2015, 05:30 PM by Geoclasm.)

(09-16-2015, 09:30 AM)mwhitney Wrote: Ignoring the oddities of your coding style and the insecurity of your ability to perform a get_where() with the user's password, the way in which you trace your chain of execution is enlightening, even if confusing. If you don't have access to $this->session in your model, the first thing that comes to mind is where are you loading the session library?

Also, though probably not relevant, is there a reason you've defined a getIp() method instead of using $this->input->ip_address()?

Hello, MWhitney - Thank you for your inquiry (and I hope help).
The session library should be loaded by the auto-loader.

The reason for the strange getIP() function is because this is not my original work - I inherited this megalithic behemoth from another team of developers. They used CI 2.X.X; I'm upgrading to CI 3.0.1.

Also, because I'm pretty much bare bones on my knowledge of PHP (I see similarities between this and other programming languages like C# and VB, things like classes and what not so I'm not completely lost), I'm going through their code and implementing it incrementally, testing it as I can at each increment.

They had created their own GetIP function - I took their function and used that, mostly because I really don't know that much about PHP...

Also - I want to say that I suspect the reason for my problem is that I do not fully understand how sessions, session IDs and session cookies work...
Reply
#4

Wow. You have taken on quite a task then.

The docs are very good on sessions:
http://www.codeigniter.com/user_guide/li...t=sessions

Sometimes, when I get a similar job to do with code that is a complete mess, I often find it quicker to map out all the functionality, and rebuild it properly from scratch.

I still have many sites running on the 2. branch of CI with no problems and have no intention of upgrading them, until absolutely necessary.

Best of luck with your task,

Paul.
Reply
#5

(This post was last modified: 09-17-2015, 07:05 AM by mwhitney.)

(09-16-2015, 05:22 PM)Geoclasm Wrote: Hello, MWhitney - Thank you for your inquiry (and I hope help).
The session library should be loaded by the auto-loader.

Is 'session' in the /application/config/autoload.php file, in the array assigned to $autoload['libraries']?

I was primarily curious about this because, once the session class is loaded, $this->session should work in any controller which extends CI_Controller (or extends a child of CI_Controller) or any model which extends CI_Model (or extends a child of CI_Model), as long as the parent constructors are called properly and none of the classes in the inheritance chain define their own session property.

Quote:The reason for the strange getIP() function is because this is not my original work - I inherited this megalithic behemoth from another team of developers.

As I said, it's probably not relevant to your issue, but, I would recommend changing the function to use CI's method, which is much more likely to get you a valid IP address:

PHP Code:
private function getIP()
{
     return $this->input->ip_address();


It would be interesting to learn what lead to your inherited getIP() method being written the way it is, but only as a curiosity. There's also a warning in the PHP manual about stacking ternary expressions as is done in that method.

Quote:They used CI 2.X.X; I'm upgrading to CI 3.0.1.

Did you make sure to follow the upgrade instructions from 2.2.x to 3.0.x very carefully? Personally, I changed my system directory when I upgraded from 2.2.x to 3.0.0, but I know that a lot of people have run into issues with the session library in particular because they just dumped the new files into their existing system directory. In CI 2.2.x, the session library is in /system/libraries/Session.php, but in CI 3.0.x, the session library is in /system/libraries/Session/Session.php, so the CI3 file doesn't overwrite the CI2 file, and the system may continue loading the CI2 library, which will usually lead to errors.

Also, since you have your sessions configured to use the database driver, make sure your sessions table is either a different table from the one you were using for CI2 sessions, or has been modified according to the documentation to work with CI3 sessions.

Quote:Also, because I'm pretty much bare bones on my knowledge of PHP (I see similarities between this and other programming languages like C# and VB, things like classes and what not so I'm not completely lost), I'm going through their code and implementing it incrementally, testing it as I can at each increment.

They had created their own GetIP function - I took their function and used that, mostly because I really don't know that much about PHP...

That's all fine, as I said I would just recommend refactoring their GetIP function to use CI's method. Although I had a little previous exposure to PHP, I really started working with it seriously in 2012. I would recommend getting very familiar with navigating CI's documentation and the PHP Manual, but the approach of upgrading an existing site will probably serve you pretty well as you try to learn PHP. Unfortunately, most of what is familiar from C# (other than general syntax) is probably relatively new in PHP, and it usually helps to remember that PHP is, at its core, a scripting language, and sometimes has more in common with JavaScript than C# (though the treatment of objects and classes is certainly closer to C# than JavaScript).

Quote:Also - I want to say that I suspect the reason for my problem is that I do not fully understand how sessions, session IDs and session cookies work...

For the most part, a session is just a mechanism within PHP to preserve data between requests. Almost every server-side web development programming language has a similar mechanism, and most of them will use a cookie as part of that mechanism (some languages, especially in ASP.Net, may also use special markup in the generated pages, and PHP can propagate session data in the URL, but generally won't do so in a CodeIgniter application).

The primary purpose of the cookie is to store the session ID, which will be matched with the session ID on the server to retrieve the actual data stored in the session (since the session data itself, in this case, is stored on the server). Most of the session configuration options in CI relate to how the server stores the session data.

The documentation for the Session library does a good job of explaining both the configuration options and sessions.

Once you get through the upgrade process and start to feel better about your understanding of the code, I would really recommend looking into changing the user authentication.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB