
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:
- Enabling session storage in CodeIgniter
- Creating the login functionality
- Creating the authentication filter
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:
- Preventing CSRF attacks in Codeigniter 4
- Protect your PHP login with Fail2Ban