Redirecting to login in Codeigniter 4

Codeigniter 4 has introduced a much easier way to redirect un-authenticated traffic on a website to a login page. It relies basically on a request filter mechanism which can perform an action “before” the initially intended action takes place.

In simple terms, what this article aims to explain, is the process in which a visitor who tries to access the admin section of your site first goes through the authentication filter and, if the user is not authenticated, they will be redirected to the login page.

In this article:

Prequisites

Before we begin, since I will not go through the whole process entirely, make sure the following conditions are met:

  • Codeigniter 4 is installed
  • You are using Twig, as described here in Integrating Twig with Codeigniter. It only affects the Login controller so, if not using Twig, change the rendering there.
  • You have created a table for users in your database and the corresponding Model

Enable session storage for authentication process

In order for the authentication process to be functional, you have to configure session storage in CodeIgniter environment. Below sets the session driver to FILE, but you can change according to your needs to :

.env (located in project root)

 app.sessionDriver = 'CodeIgniter\Session\Handlers\FileHandler'
 app.sessionCookieName = 'ci_session'
 app.sessionExpiration = 7200
 app.sessionMatchIP = false
 app.sessionTimeToUpdate = 300
 app.sessionRegenerateDestroy = false

then, in your BaseController you need to instantiate and load the session driver by adding the two highlighted items:

app/Controllers/BaseController.php

<?php

.....
abstract class BaseController extends Controller
{
    ....
    protected $session;
    ....
    public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
    {
        // Do Not Edit This Line
        parent::initController($request, $response, $logger);
        ....
        $this->session = \Config\Services::session();
        ....
    }
}

Create the login functionality

We need to add a controller that handles displaying the login page as well as checks the login form user data against the users database and, if the user is found, it will save the user to session.

app/Controllers/Login.php

<?php

namespace App\Controllers;

use App\Models\Users;

class Login extends BaseController
{
    public function index()
    {
        return $this->twig->render('login.html.twig', $this->twig_data);              // Twig rendering here, change if you are using default
    }

    public function logout(){
        $this->session->destroy();
        return $this->twig->render('login.html.twig', $this->twig_data);               // Twig rendering here, change if you are using default
    }

    public function checkLogin(){
        $username = $this->request->getVar('username');
        $password = $this->request->getVar('password');
        $user_class = new Users();
        $user = $user_class->findWhere(['username'=>$username]);
        
       if($user && password_verify($password,$user['password'])){
               $this->session->set('user',$user);
               return redirect()->to(base_url());
       } else {
               $this->session->setFlashdata('error','Keep trying..');
               return redirect()->to(base_url().'/login');
       }
    }

}

Make sure you add the findWhere() function to your BaseModel or Users model (which should extend BaseModel) otherwise the above will throw an error:

app/Models/BaseModel.php

public function findWhere($where) {
        $builder = $this->builder();
        return $builder->where($where)->limit(1)->get()->getRowArray();
    }

Now let’s create the login view (again this is using Twig for rendering). You can beautify it of course with Bootstrap but that is out of our scope:

app/Views/login.html.twig

<html>
    <head>
        <title>Login to Sample site</title>
    </head>
    <body>
        <div>
            <form id="login_form" name="login_form" method="post" action="checklogin">
                <!-- Username input -->
                <div>
                    <label for="username">Username</label>
                    <input type="text" name="username" id="username" />
                </div>
                <!-- Password input -->
                <div>
                    <label for="password">Password</label>
                    <input type="password" name="password" id="password" />
                </div>

                <button type="submit">Sign in</button>
            </form>
        </div>
    </body>
</html>

Now create the CodeIgniter route for the Login page as well as for the checkLogin action. Note that for the checkLogin function we only allow POST requests. Add the following lines:

app/Config/Routes.php

$routes->get('/login','Login::index');
$routes->get('/logout','Login::logout');
$routes->post('/checklogin','Login::checkLogin');

Creating the authentication filter

So far, so good, we are able to authenticate if we access the login page, however, at this point, we can roam freely on the website with full access to the admin section. Here comes the good stuff.

As explained in the beginning, CodeIgniter has introduced Filters for requests so, what we will do, is create a filter for any requests going to the admin part of the site.

Step 1: Creating the Filter class

There are two options here, either one being equally valid. First option is to create the filter class using Spark:

spark make:filter Auth

or, you can manually create your Auth filter:

app/Filters/Auth.php

<?php

namespace App\Filters;

use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;

class Auth implements FilterInterface
{

    public function before(RequestInterface $request, $arguments = null)
    {
        if(! session()->get('user')){
            return redirect()->to(base_url().'/login');
        }
    }

    public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
    {
        //
    }
}

Step 2: adding Auth filter to the known filters for CodeIgniter

app/Config/Filters.php add the following as shown:

....
class Filters extends BaseConfig
{
    ....
    public $aliases = [
        'csrf'          => CSRF::class,
        'toolbar'       => DebugToolbar::class,
        'honeypot'      => Honeypot::class,
        'invalidchars'  => InvalidChars::class,
        'secureheaders' => SecureHeaders::class,
        'auth'          => Auth::class,
    ];
    ....
}

Step 3: filter any requests going to /admin via the newly created filter

I am assuming you already have extended BaseController with a, let’s say, Home controller in which you define all your pages. As you know, BaseController is not routable.

In app/Config/Routes.php change the reference for /admin from:

$routes->get('/admin', 'Home::admin');

to this:

$routes->get('/admin', 'Home::admin', ['filter'=>'auth']);

Now, assuming you have not previously logged in (your user was not saved to the session), every time you navigate to /admin you will be redirected to the login page.

Go ahead and try it!

More useful resources:


Posted

in

,

by