In most web applications, securing user data and managing access to specific areas of the system is critical. While CodeIgniter 4 provides third-party packages like Shield, many developers prefer building custom authentication systems for flexibility, learning, or lightweight project needs.
In this guide, we’ll walk through:
- Database setup for users
- Creating a registration system
- Secure password hashing
- Login system with session management
- Logout functionality
- Protecting routes from unauthorized access
This will help you build a secure, flexible, and maintainable authentication system from scratch.
Let’s get started.
π Database Table Structure
We’ll begin by setting up a users table to store basic user information.
Create a Migration:
php spark make:migration CreateUsersTable
Migration Code:
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'auto_increment' => true],
'username' => ['type' => 'VARCHAR', 'constraint' => '100'],
'email' => ['type' => 'VARCHAR', 'constraint' => '100', 'unique' => true],
'password' => ['type' => 'VARCHAR', 'constraint' => '255'],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
Run Migration:
php spark migrate
Read More: CodeIgniter 4 Database Seeders
π Setup User Model
The model will interact with the users table and manage data retrieval and insertion.
Create Model:
php spark make:model UserModel
Model Code:
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $allowedFields = ['username', 'email', 'password', 'created_at', 'updated_at'];
}
π Registration & Login Controller: Setup and Explanation
To manage both user registration and login logic, we’ll create a controller named Auth. CodeIgniter 4’s Spark CLI can quickly generate this file for us.
Create Controller:
php spark make:controller Auth
This command will automatically create a new file named Auth.php inside the following directory app/Controllers
This controller will handle both registration and login functionalities by creating two separate functions inside it:
register() β to manage user signup, validation, password hashing, and saving the user to the database.
login() β to handle verifying user credentials, starting a session, and redirecting authenticated users to a secure page.
>> Registration Method:
public function register()
{
helper(['form']);
if ($this->request->getMethod() == 'post') {
$rules = [
'username' => 'required|min_length[3]|max_length[50]',
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[6]',
'confirm_password' => 'matches[password]',
];
if (!$this->validate($rules)) {
return view('auth/register', ['validation' => $this->validator]);
} else {
$model = new \App\Models\UserModel();
$data = [
'username' => $this->request->getVar('username'),
'email' => $this->request->getVar('email'),
'password' => password_hash($this->request->getVar('password'), PASSWORD_DEFAULT),
];
$model->save($data);
return redirect()->to('/login')->with('success', 'Registration successful!');
}
}
return view('auth/register');
}
Sample View: app/Views/auth/register.php
<h2>Register</h2>
<?= session()->getFlashdata('success') ?>
<form action="<?= site_url('register') ?>" method="post">
<input type="text" name="username" placeholder="Username"><br>
<input type="email" name="email" placeholder="Email"><br>
<input type="password" name="password" placeholder="Password"><br>
<input type="password" name="confirm_password" placeholder="Confirm Password"><br>
<button type="submit">Register</button>
</form>
<?= isset($validation) ? $validation->listErrors() : '' ?>
>> Login Method:
Now let’s authenticate users by verifying passwords and setting session data.
public function login()
{
helper(['form']);
if ($this->request->getMethod() == 'post') {
$session = session();
$model = new \App\Models\UserModel();
$user = $model->where('email', $this->request->getVar('email'))->first();
if ($user && password_verify($this->request->getVar('password'), $user['password'])) {
$sessionData = [
'id' => $user['id'],
'username' => $user['username'],
'email' => $user['email'],
'isLoggedIn' => true,
];
$session->set($sessionData);
return redirect()->to('/dashboard');
} else {
return redirect()->back()->with('error', 'Invalid login credentials');
}
}
return view('auth/login');
}
Sample View: app/Views/auth/login.php
<h2>Login</h2>
<?= session()->getFlashdata('error') ?>
<form action="<?= site_url('login') ?>" method="post">
<input type="email" name="email" placeholder="Email"><br>
<input type="password" name="password" placeholder="Password"><br>
<button type="submit">Login</button>
</form>
π Logout Function
When a user logs into your application, their details like id, username, email, and a flag like isLoggedIn are typically stored in the session.
When the user wants to log out, we need to destroy their session so no sensitive data remains accessible, and redirect them back to the login page.
public function logout()
{
session()->destroy();
return redirect()->to('/login');
}
π Routes for Authentication
Here’s how to set up routes for register, login, and logout.
>> Registration Route
$routes->match(['get', 'post'], 'register', 'Auth::register');
π Explanation:
- Listens for both GET and POST requests to /register.
- Calls the register() method inside Auth.php.
- GET request loads the form, POST request handles form submission.
>> Login Route
$routes->match(['get', 'post'], 'login', 'Auth::login');
π Explanation:
- Listens for both GET and POST requests to /login.
- Calls the login() method inside Auth.php.
- GET request displays the login form, POST request verifies credentials.
>> Logout Route
$routes->get('logout', 'Auth::logout');
π Explanation:
- Listens only for a GET request to /logout.
- Calls the logout() method inside Auth.php.
- This clears the session and redirects the user to the login page.
π Protecting Routes (Middleware / Filter)
In CodeIgniter 4, Filters act like middleware β they allow you to run code before or after a controller method executes.
For example:
- Before accessing a dashboard, we can check if a user is logged in.
- If not, redirect them to the login page.
This ensures only authenticated users can access restricted routes.
>> Create Filter:
php spark make:filter AuthCheck
This command will automatically create a new file named AuthCheck.php inside app/Filters
This file will contain the logic to check the session and redirect unauthenticated users.
Filter Code:
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class AuthCheck implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
if (!session()->get('isLoggedIn')) {
return redirect()->to('/login');
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {}
}
π Explanation:
- The before() method runs before accessing any route where this filter is applied.
- It checks if isLoggedIn exists in the session.
- If not, it redirects the user to /login.
>> Register the Filter in Config
Now, we need to register this custom filter in the project configuration.
Open app/Config/Filters.php file. Inside the $aliases array,
public $aliases = [
'auth' => \App\Filters\AuthCheck::class,
];
π Explanation:
- This creates a short alias ‘auth’ that we can use in the routes file instead of writing the full class path.
Apply to Routes
$routes->get('/dashboard', 'Dashboard::index', ['filter' => 'auth']);
π Explanation:
- This ensures that the /dashboard route is only accessible if the user is logged in.
- If someone tries to visit it without logging in, the filter will redirect them to the /login page.
π Conclusion
We now have a fully functional custom authentication system in CodeIgniter 4 β complete with registration, login, logout, session management, and route protection without third-party packages.
This approach keeps your system lightweight, gives you control over logic, and allows customization as your application grows.