build an authentication system in CodeIgniter 4

How to build an authentication system in CodeIgniter 4

In this article I am going to take you through the topic of how to build an authentication system in CodeIgniter 4 title, more specifically (CodeIgniter 4.4.4).

If you are building a web application for an organization or it’s a personal project, it might require an authentication system.

If you are exploring how to build an authentication system in CodeIgniter 4, you are at the right place.

In this article we will create the project by the help of composer, and will use spark to run the migration, creating controllers and models.

We will provide step by step guide on how to install the codeigniter 4, creating database connection and creating migration.

These steps are for those who are new to codeigniter and wants to install a fresh project, if you are the one who already has codeigniter project, just skip the first or two steps.

Let’s dive into how to build an authentication system in CodeIgniter 4 process.

Step 1: Install CodeIgniter 4

You can install fresh codeigniter project with the help of below command.

composer create-project codeigniter4/appstarter AuthProject

Step 2: configure database connection

To set up a database connection, you will need to use XAMPP or WAMPP , then go to your phpMyAdmin and create a database by the name of ci_auth .

For database connection, you have two options.

First Option: change the name of the env file to the .env file, and uncomment the database parts, and provide your database specification.

Second Option: go to the app/config folder and open the Database.php file, then type your database specifications and credentials, or paste the below codes into it.

<?php

namespace Config;

use CodeIgniter\Database\Config;

/**
 * Database Configuration
 */
class Database extends Config
{
    /**
     * The directory that holds the Migrations
     * and Seeds directories.
     */
    public string $filesPath = APPPATH . 'Database' . DIRECTORY_SEPARATOR;

    /**
     * Lets you choose which connection group to
     * use if no other is specified.
     */
    public string $defaultGroup = 'default';

    /**
     * The default database connection.
     */
    public array $default = [
        'DSN'          => '',
        'hostname'     => 'localhost',
        'username'     => 'root',
        'password'     => '',
        'database'     => 'ci_auth',
        'DBDriver'     => 'MySQLi',
        'DBPrefix'     => '',
        'pConnect'     => false,
        'DBDebug'      => true,
        'charset'      => 'utf8',
        'DBCollat'     => 'utf8_general_ci',
        'swapPre'      => '',
        'encrypt'      => false,
        'compress'     => false,
        'strictOn'     => false,
        'failover'     => [],
        'port'         => 3306,
        'numberNative' => false,
    ];

    /**
     * This database connection is used when
     * running PHPUnit database tests.
     */
    public array $tests = [
        'DSN'         => '',
        'hostname'    => '127.0.0.1',
        'username'    => '',
        'password'    => '',
        'database'    => ':memory:',
        'DBDriver'    => 'SQLite3',
        'DBPrefix'    => 'db_',  // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
        'pConnect'    => false,
        'DBDebug'     => true,
        'charset'     => 'utf8',
        'DBCollat'    => 'utf8_general_ci',
        'swapPre'     => '',
        'encrypt'     => false,
        'compress'    => false,
        'strictOn'    => false,
        'failover'    => [],
        'port'        => 3306,
        'foreignKeys' => true,
        'busyTimeout' => 1000,
    ];

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

        // Ensure that we always set the database group to 'tests' if
        // we are currently running an automated test suite, so that
        // we don't overwrite live data on accident.
        if (ENVIRONMENT === 'testing') {
            $this->defaultGroup = 'tests';
        }
    }
}

Step 3: Make your migration file

For creating a table in CodeIgniter we need to have a migration file, to migrate and create our table. To do this run the following command in your terminal.

php spark make:migration AddUsers

After running the above command, go to the app/Database/Migrations directory and open the migration that you made.

And paste the below codes into it and save it.

<?php

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class AddUsers extends Migration
{
    public function up()
    {
        $this->forge->addField([
            'id' => ['type' => 'INT', 'constraint' => 5, 'unsigned' => true, 'auto_increment' => true],
            'name' => ['type' => 'VARCHAR', 'constraint' => 190],
            'email' => ['type' => 'VARCHAR', 'constraint' => 1900],
            'password' => ['type' => 'VARCHAR', 'constraint' => 190],
            'created_at' => ['type' => 'datetime'],
        ]);
        $this->forge->addKey('id', true);
        $this->forge->createTable('users');
    }

    public function down()
    {
        $this->forge->dropTable('users');
    }
}

After saving the migration file, run the below command to make the user’s table and its attributes.

php spark migrate

Step 4: Create your controller

At this step, we need to make a controller for our project. To make a controller in CodeIgnter, you need to run the below command.

php spark make:controller AuthController

After running the above command CodeIgniter will create a file in the app/Controller directory, open the AuthController.php file, and paste the below codes into it.

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Models\User;
use Config\Services;
use Config\Session;


class AuthController extends BaseController
{
    public function __construct()
    {

        $this->userModel = new \App\Models\User();
        $this->session = \Config\Services::session();
        if (isset($_SESSION['loggedIn'])) {
            return redirect()->to('/dashboard');
        } else {
            return redirect()->to('/login');
        }
    }
    public function index()
    {
        if (isset($_SESSION['loggedIn'])) {
            return redirect()->to('/dashboard');
        } else {
            return redirect()->to('/login');
        }
    }
    //login 
    public function login()
    {
        if (isset($_SESSION['loggedIn'])) {
            return redirect()->to('/dashboard');
        } else {
            return view('login');
        }
    }
    public function logout()
    {
        session_destroy();
        return redirect()->to('/login');
    }
    //register logic
    public function register()
    {
        if (isset($_SESSION['loggedIn'])) {
            return redirect()->to('/dashboard');
        } else {
            return view('register');
        }
    }
    public function dashboard()
    {
        if (isset($_SESSION['loggedIn'])) {
            return view('dashboard', ['title' => 'dashboard']);
        } else {
            return redirect()->to('/login');
        }
    }
    //login page
    public function doLogin()
    {
        $email = $this->request->getVar('email');
        $password = $this->request->getVar('password');
        $user = $this->userModel->verifyUser($email, $password);
        if (!empty($user->resultID->num_rows > 0) || $user->resultID->num_rows > 0) {
            $data = $user->getResult();
            if (!password_verify($password, $data[0]->password)) {
                $this->session->set('danger', 'Incorrect password');
                return redirect()->to('/login');
            } else {
                $sessiondata = [
                    'name' => $data[0]->name,
                    'email' => $email,
                    'loggedIn' => true,
                ];
                $this->session->set($sessiondata);
                return redirect()->to('/dashboard');
            }
        } else {
            $this->session->set('danger', 'User does not exist');
            return redirect()->to('/login');
        }
    }
    //register page
    public function doRegister()
    {

        $data = [
            'name' => $this->request->getVar('uname'),
            'email' => $this->request->getVar('email'),
            'password' => $this->request->getVar('password')
        ];
        $iserted = $this->userModel->createUser($data);
        if ($iserted) {

            $this->session->set('success', 'Account registered successfully');
            return redirect()->to('/login');
        } else {
            $this->session->set('danger', 'Failed to register, try again latter');
            return redirect()->to('/register');
        }
    }
}

Step 5: Create users model

For interacting with the database, in codeigniter we need to do our database stuff and queries into the models, therefore we need to create a model at this step.

For creating a model, we need to run the following command

php spark make:model

After running the above command codeigniter will automatically create a model into the app/Models directory.

Now, open the Users.php file into the Models directory and paste the below codes into it.

<?php

namespace App\Models;

use CodeIgniter\Model;

class User extends Model
{
    protected $DBGroup          = 'default';
    protected $table            = 'users';
    protected $primaryKey       = 'id';
    protected $useAutoIncrement = true;
    protected $insertID         = 0;
    protected $returnType       = 'array';
    protected $useSoftDeletes   = false;
    protected $protectFields    = true;
    protected $allowedFields    = ['name', 'email', 'password'];

    // Dates
    protected $useTimestamps = false;
    protected $dateFormat    = 'datetime';
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';
    protected $deletedField  = 'deleted_at';

    // Validation
    protected $validationRules      = [
        'name' => 'required',
        'password' => 'required|min_length[6]',
        'email'    => 'required|valid_email',
    ];
    protected $validationMessages   = [
        'name' => 'username required',
        'password' => 'password required',
        'email' => 'email required',
    ];
    protected $skipValidation       = false;
    protected $cleanValidationRules = true;

    // Callbacks
    protected $allowCallbacks = true;
    protected $beforeInsert   = [];
    protected $afterInsert    = [];
    protected $beforeUpdate   = [];
    protected $afterUpdate    = [];
    protected $beforeFind     = [];
    protected $afterFind      = [];
    protected $beforeDelete   = [];
    protected $afterDelete    = [];

    public function createUser($param = array())
    {
        $password = password_hash($param['password'], PASSWORD_BCRYPT);
        $data = ['name' => $param['name'], 'email' => $param['email'], 'password' => $password];
        $inserted = $this->save($data);

        if ($inserted) {
            return true;
        } else {

            return false;
        }
    }

    public function verifyUser($email, $password)
    {

        return $this->table($this->table)->where('email', $email)->get();
    }
}

Step 6: create your routes

In codeigniter 4 we need to create route for each request we make, in codeigniter 3 , we didn’t need to create route, the route in codeigniter and older versions was the controller name plus method/function name. but in codeigniter 4 we can name routes as we want.

For this project you need to open Routes.php file from app/Config directory and add the following routes into it.

<?php

namespace Config;

// Create a new instance of our RouteCollection class.
$routes = Services::routes();

// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (is_file(SYSTEMPATH . 'Config/Routes.php')) {
 require SYSTEMPATH . 'Config/Routes.php';
}

/*
 * --------------------------------------------------------------------
 * Router Setup
 * --------------------------------------------------------------------
 */
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('Home');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
// The Auto Routing (Legacy) is very dangerous. It is easy to create vulnerable apps
// where controller filters or CSRF protection are bypassed.
// If you don't want to define all routes, please use the Auto Routing (Improved).
// Set `$autoRoutesImproved` to true in `app/Config/Feature.php` and set the following to true.
// $routes->setAutoRoute(false);

/*
 * --------------------------------------------------------------------
 * Route Definitions
 * --------------------------------------------------------------------
 */

// We get a performance increase by specifying the default
// route since we don't have to scan directories.

$routes->get('/', 'AuthController::index');
$routes->get('/login', 'AuthController::login');
$routes->post('/login/auth', 'AuthController::doLogin');
$routes->get('/logout', 'AuthController::logout');
$routes->get('/register', 'AuthController::register');
$routes->get('/dashboard', 'AuthController::dashboard');
$routes->post('/register/auth', 'AuthController::doRegister');

/*
 * --------------------------------------------------------------------
 * Additional Routing
 * --------------------------------------------------------------------
 *
 * There will often be times that you need additional routing and you
 * need it to be able to override any defaults in this file. Environment
 * based routes is one such time. require() additional route files here
 * to make that happen.
 *
 * You will have access to the $routes object within that file without
 * needing to reload it.
 */
if (is_file(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
 require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}

You may be interested on building Inertia Jetstream authentication in laravel 10

Step 7: create view files

At this step we need to have our login page, register and dashboard page file.

In CodeIgniter the view files are located into app/Views directory. You need to create 3 view files in this directory.

First, create a login file and name it login.php file, and paste the following codes into it.

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>login</title>
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
 <link rel="stylesheet" href="css/css.css">
</head>

<body>
 
 <section class="vh-100">
  <div class="container-fluid h-custom">
   <div class="row d-flex justify-content-center align-items-center h-100">
    <div class="col-md-9 col-lg-6 col-xl-5">
     <img src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-login-form/draw2.webp" class="img-fluid" alt="Sample image">
    </div>
    <div class="col-md-8 col-lg-6 col-xl-4 offset-xl-1">
     <form action="login/auth" method="POST">
      <?php if (isset($_SESSION['success'])) { ?>
       <div class="alert alert-success" role="alert">
        <?php echo $_SESSION['success'];
        unset($_SESSION['success']);
        ?>


       </div>
      <?php } ?>
      <?php
      if (!empty($errors)) { ?>
       <div class="alert alert-danger" role="alert">
        <ul>
         <?php foreach ($errors as $error) :
         ?>
          <li><?= esc($error) ?></li>
         <?php
         endforeach ?>
        </ul>
       </div>
      <?php } ?>
      <?php if (isset($_SESSION['danger'])) { ?>
       <div class="alert alert-danger" role="alert">
        <?php echo $_SESSION['danger'];
        unset($_SESSION['danger']);
        ?>
       </div>
      <?php }

      ?>
      <div>
       <h3>Log in to the system</h3>
       <br>
      </div>
      <!-- Email input -->
      <div class="form-outline mb-4">
       <input type="email" id="form3Example3" name="email" class="form-control form-control-lg" placeholder="Email" />
      </div>

      <!-- Password input -->
      <div class="form-outline mb-3">
       <input type="password" id="form3Example4" name="password" class="form-control form-control-lg" placeholder="Password" />
      </div>

      <div class="text-center text-lg-start mt-4 pt-2">
       <button type="submit" class="btn btn-primary btn-lg" style="padding-left: 2.5rem; padding-right: 2.5rem;">Login</button>
       <p class="small fw-bold mt-2 pt-1 mb-0">Don't have an account? <a href="register" class="link-danger">Register</a></p>
      </div>

     </form>
    </div>
   </div>
  </div>
  <div class="d-flex flex-column flex-md-row text-center text-md-start justify-content-between py-4 px-4 px-xl-5 bg-primary">
   <!-- Copyright -->
   <div class="text-white mb-3 mb-md-0">
    Copyright © 2020. All rights reserved.
   </div>
   <!-- Copyright -->


  </div>
 </section>
</body>

</html>

Then, create a file by the name of register.php and paste these codes.

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Register</title>
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

 <link rel="stylesheet" href="css/css.css">
</head>

<body>
 
 <section class="vh-100">
  <div class="container-fluid h-custom">
   <div class="row d-flex justify-content-center align-items-center h-100">
    <div class="col-md-9 col-lg-6 col-xl-5">
     <img src="https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-login-form/draw2.webp" class="img-fluid" alt="Sample image">
    </div>
    <div class="col-md-8 col-lg-6 col-xl-4 offset-xl-1">
     <form action="register/auth" method="POST">
      <?php
      if (!empty($errors)) { ?>
       <div class="alert alert-danger" role="alert">
        <ul>
         <?php foreach ($errors as $error) :
         ?>
          <li><?= esc($error) ?></li>
         <?php
         endforeach ?>
        </ul>
       </div>
      <?php } ?>
      <?php if (isset($_SESSION['danger'])) { ?>
       <div class="alert alert-danger" role="alert">
        <?php echo $_SESSION['danger'];
        unset($_SESSION['danger']);
        ?>
       </div>
      <?php }

      ?>
      <?= csrf_field() ?><form action="register/auth" method="POST">
       <?php
       if (!empty($errors)) { ?>
        <div class="alert alert-danger" role="alert">
         <ul>
          <?php foreach ($errors as $error) :
          ?>
           <li><?= esc($error) ?></li>
          <?php
          endforeach ?>
         </ul>
        </div>
       <?php } ?>
       <?php if (isset($_SESSION['danger'])) { ?>
        <div class="alert alert-danger" role="alert">
         <?php echo $_SESSION['danger'];
         unset($_SESSION['danger']);
         ?>
        </div>
       <?php }

       ?>
       <?= csrf_field() ?>

       <div>
        <h3>Register yourself with our system</h3>
        <br>
       </div>
       <!-- Name input -->
       <div class="form-outline mb-4">
        <input type="tex" id="form3Example2" name="uname" class="form-control form-control-lg" placeholder="Enter your full name" />
       </div>

       <!-- Email input -->
       <div class="form-outline mb-4">
        <input type="email" id="form3Example3" name="email" class="form-control form-control-lg" placeholder="Email " />
       </div>

       <!-- Password input -->
       <div class="form-outline mb-3">
        <input type="password" id="form3Example4" name="password" class="form-control form-control-lg" placeholder="Password" />
       </div>

       <div class="text-center text-lg-start mt-4 pt-2">
        <button type="submit" class="btn btn-primary btn-lg" style="padding-left: 2.5rem; padding-right: 2.5rem;">Register</button>
        <p class="small fw-bold mt-2 pt-1 mb-0">Have an account? <a href="login" class="link-danger">Login</a></p>
       </div>

      </form>
    </div>
   </div>
  </div>
  <div class="d-flex flex-column flex-md-row text-center text-md-start justify-content-between py-4 px-4 px-xl-5 bg-primary">
   <!-- Copyright -->
   <div class="text-white mb-3 mb-md-0">
    Copyright © 2020. All rights reserved.
   </div>
   <!-- Copyright -->

  </div>
 </section>
</body>

</html>

And finally, create a Dashboard.php file, and paste the following code into it.

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title><?= esc($title) ?></title>
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>

<body>
 <nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container">
   <a class="navbar-brand" href="#">Dashboard</a>
   <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
   </button>
   <div class="collapse navbar-collapse" id="navbarNav">
    <ul class="navbar-nav">
     <li class="nav-item active">
      <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
     </li>

     <li class="nav-item">
      <a class="nav-link " href="/logout">Logout</a>
     </li>
    </ul>
   </div>
  </div>
 </nav>
 <div class="row d-flex align-items-center justify-content-center" style="min-height: 100vh;">
  <h3>Welcome <?= $_SESSION['name'] ?></h3>

 </div>
</body>

</html>

Step 8: create your CSS file

For styling, we need to have our css files, the css files are contained in public directory in root directory of your project.

For this project, you need to create a directory by the name of css and a file by the name of css.css into it and paste the below cascading stylesheet codes.

.divider:after,
.divider:before {
  content: '';
  flex: 1;
  height: 1px;
  background: #eee;
}
.h-custom {
  height: calc(100% - 73px);
}
@media (max-width: 450px) {
  .h-custom {
    height: 100%;
  }
}

You are done with the authentication system.

Now you need to run your codeigniter project and see the result. Use this command to start your CodeIgniter built-in dev server

php spark serve

Or you can access your project directly from the browser if you located your project in XAMPP or WAMP deve servers. Now you will have the courage on how to build an authentication system in CodeIgniter 4

Happy coding, hope this was useful for you.

Leave a Reply

Your email address will not be published. Required fields are marked *