Scalable Login System for CodeIgniter – Ion_Auth

*This article assumes you are familiar with the structure and usage of the CodeIgniter MVC Framework and basic OOP techniques

If you are building an application that will live on the web, at some point you will most likely need to implement a way for users to log in, even if it’s a single user. This “How-to” will appeal to two different groups of people

  • “Dude, forget ‘Scalable’, just show me how to add log in functionality to my application.”
  • “I’ve got the log in thing figured out, but there has to be a better way!”

These were quotes from my mouth at two different points in my progression as a developer that prefers to use CodeIgniter.  What I’d like to share, is my light-bulb moment in the realm of user authentication.  I’ll quickly run you through how to get the basics of Ion_Auth (my preferred authentication library) up and running.  Then I’ll get more into the “Ah ha!” for me, which was how you structure your Controllers to limit/eliminate the constant need to make sure the visitor is an authenticated user.

Setting up Ion_Auth

If you already have a authentication system up and running, feel free to skip the the next section. You will just need to replace Ion_Auth’s logic with your system’s logic. Ion_Auth – Download Here - GitHub Straight from the Ion_Auth ReadMe

Just copy the files from this package to the correspoding folder in your application folder. For example, copy Ion_auth/config/ion_auth.php to system/application/config/ion_auth.php.

Once you have the files copied, make sure you use the proper .sql file to create the needed database tables in your database.  Then you need to take a look at system/application/config/ion_auth.php you do not neccessarily need to change anything off the bat, but be sure to familiarize yourself with it. One more thing before it is ready for use, make sure you have the proper dependancies autoloaded along with ion_auth in system/application/config/autoload.php

$autoload['libraries'] = array('database','session','email','ion_auth');

Now that you have have Ion_Auth available for use, you can review the docs and example files in order to more fully utilize the library, for now I will be simply showing how to setup your “protected” controllers once you have registered users.

Constructing your Controllers

Generally, with Ion_Auth you just use

$this->ion_auth->logged_in();

to check if a user is logged in.  It returns a boolean True/False and then you act accordingly.  When I first started using this system with CodeIgniter, I was putting this check in every function of my Controllers.  This seemed ugly and redundant, so in an attempt to streamline and re-use code, I would construct a private function for the controller and then would just call that function at the beginning of every function, saving a few lines of code. Then the natural progression for me moved the authentication into the constructor of the Controller, which again seemed good, but even still was a block of code that was copy/pasted to every controller constructor.  That is when the light-bulb went off.  I had read about how to extend CodeIgniter libraries, but what I realized is that the Controller is just another library. You extend CodeIgniter libraries with the Prefix you define in your system/application/config/config.php which is by default “MY_”.  If this feels over your head, it is not.  Just stick with me and you’ll see it is very simple. I will go ahead and start in and show you.  If I want to extend the base Controller, contained in system/core/Controller.php, to contain my authentication codez, I would simply create MY_Controller.php in system/application/core/ I will show you an extended Controller contained within MY_Controller.php

class Admin_Controller extends CI_Controller {

    //Class-wide variable to store user object in.
    protected $the_user;

    public function __construct() {

        parent::__construct();

        //Check if user is in admin group
        if ( $this->ion_auth->is_admin() ) {

            //Put User in Class-wide variable
            $this->the_user = $this->ion_auth->user()->row();

            //Store user in $data
            $data->the_user = $this->the_user;

            //Load $the_user in all views
            $this->load->vars($data);
        }
        else {
            redirect('/');
        }
    }
}

Here you can see it looks like a normal controller you would make in your controllers directory, but I’ve wrapped in the auth logic in the constructor and created a class-wide variable $this->the_user to give the controller global access to the currently logged in user. When I did this in each and every controller I would create, my class-wide variable was set to private, but because this is a base from which I plan to extend I have made it protected to allow the controllers that extend this one to have access. Also I have used $this->load->vars() to load $the_user into all views served up by this controller. (now you can use $the_user in all views to represent your user object, $the_user->first_name, etc…) Also, you can setup as many base controllers as you like in MY_Controller.php.  Below, I’ll show you a copy of my basic setup that utilizes 2 types of users (admins, users), and also provides a base controller for those types to share for common tasks between them (like editing the user profile, changing password, etc…). Just click “MY_Controller.php” to expand the code if you’re insterested.

class Admin_Controller extends CI_Controller {

    protected $the_user;

    public function __construct() {

        parent::__construct();

        if($this->ion_auth->is_admin()) {
            $this->the_user = $this->ion_auth->user()->row();
            $data->the_user = $this->the_user;
            $this->load->vars($data);
        }
        else {
            redirect('/');
        }
    }
}

class User_Controller extends CI_Controller {

    protected $the_user;

    public function __construct() {

        parent::__construct();

        if($this->ion_auth->in_group('user')) {
            $this->the_user = $this->ion_auth->user()->row();
            $data->the_user = $this->the_user;
            $this->load->vars($data);
        }
        else {
            redirect('/');
        }
    }
}

class Common_Auth_Controller extends CI_Controller {

    protected $the_user;

    public function __construct() {

        parent::__construct();

        if($this->ion_auth->logged_in()) {
            $this->the_user = $this->ion_auth->user()->row();
            $data->the_user = $this->the_user;
            $this->load->vars($data);
        }
        else {
            redirect('/');
        }
    }
}

Implementing your extended Controllers

So you’ve got some Controllers sitting my MY_Controller.php, now what? Use em! The first consideration is how I structure my controller directories, which allows for one level of directory organization.

application/
 → controllers/
      → admin/
           → home.php
      → user/
           → home.php
      → common_auth/
           → all_user_groups_can_use_this_stuff.php
      → public/ 
           → auth.php 
    ...
...

Notice I have a subfolder for each type of auth group, a common_auth folder for controllers that have shared functionality, and a public folder that is self explanitory.

So now lets create a simple Auth controller to process a login:

class Auth extends CI_Controller {

    function __construct() {
        parent::__construct();
    }

    function index() {
        redirect('/');
    }

    /**
     * Global Login function to log user in and direct to proper area
     *
     * @return void
     * @author Jonathan Johnson
     **/
    function login() {

        if($_POST) {   //clean public facing app input
            $identity = $this->input->post('identity', true);
            $password = $this->input->post('password', true);

            //Ion_Auth Login fun
            if($this->ion_auth->login($identity,$password)) {

                //capture the user
                $user = $this->ion_auth->user()->row();

                redirect($user->group.'/home');

                /*redirect to the proper home
                  controller using the user
                  groups as folder names */
            }
            else {

                // set error flashdata
                $this->session->set_flashdata(
                    'error',
                    'Your login attempt failed.'
                );

                redirect('/');
            }
        }
        redirect('/');
    }

    /**
     * Global logout function to destroy user session
     *
     * @return void
     * @author Jonathan Johnson
     **/
    function logout() {   //Basic Ion_Auth Logout function
        $this->ion_auth->logout();
        redirect('/');
    }

}

Now that we have the user redirected to their respective areas, we need to build the controllers to receive them.  Here I will extend one of our Controllers in MY_Controller.php, and build the “Home” controller for an Admin.

class Home extends Admin_Controller {

    function __construct() {
        parent::__construct();
    }

    function index() {
        // do stuff here -
        // remember that $this->the_user is available in this controller
        // as is $the_user available in any view I load
    }

}

Notice on line three, I am not extending Controller, I am extending our “Admin_Controller” that lives in MY_Controller.php. Remember you can do this with all three (or more) controllers you have in your custom Controller library.

 

Working Example using CI 2.x and Ion_Auth 2.x -> https://github.com/jondavidjohn/auth_example

Follow the commit history, tried to make it as linear and easy to follow as possible…

Follow me on Twitter ( ) or subscribe via RSS ( ).