Securing your mobile API is critical in modern applications. In this guide, we’ll walk through how to implement JWT (JSON Web Token) based authentication in CodeIgniter 3, including access token and refresh token support for long-lived sessions.
Overview of JWT Auth Flow
Here’s the standard flow:
- User logs in → server returns an access token and a refresh token.
- Mobile app uses the access token in the
Authorization
header for every request. - When access token expires, the app sends the refresh token to get a new access token.
Prerequisites
- CodeIgniter 3 installed
firebase/php-jwt
JWT library via Composerusers
table for authentication anduser_tokens
table for refresh tokens
Step 1: Install JWT Library
Use composer to install JWT library as follows:
composer require firebase/php-jwt
Step 2: Create JWT Helper Class
Create a JWT helper class file at application/libraries/Authorization_Token.php
and add the following code to it:
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class Authorization_Token {
private $CI;
private $token_key;
public function __construct() {
$this->CI =& get_instance();
$this->token_key = 'YOUR_SECRET_KEY';
}
public function generateToken($user_data) {
$issuedAt = time();
$expirationTime = $issuedAt + 3600; // 1 hour
$payload = [
'iat' => $issuedAt,
'exp' => $expirationTime,
'data' => $user_data
];
return JWT::encode($payload, $this->token_key, 'HS256');
}
public function validateToken() {
$headers = apache_request_headers();
if (!isset($headers['Authorization'])) return false;
$token = str_replace('Bearer ', '', $headers['Authorization']);
try {
$decoded = JWT::decode($token, new Key($this->token_key, 'HS256'));
return (array) $decoded->data;
} catch (Exception $e) {
return false;
}
}
}
Step 3: Create Login API
Create a user_tokens table for storing refresh tokens.
CREATE TABLE user_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
refresh_token VARCHAR(255) NOT NULL,
expires_at DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
Create a login API file anywhere inside app/controllers
folder and add the following content inside it.
class Auth extends CI_Controller {
public function login_post()
{
$email = $this->post('email');
$password = $this->post('password');
$user = $this->db->get_where('users', ['email' => $email])->row();
if (!$user || !password_verify($password, $user->password)) {
return $this->response(['status' => false, 'message' => 'Invalid credentials'], 401);
}
$this->load->library('Authorization_Token', null, 'authToken');
$access_token = $this->authToken->generateToken(['id' => $user->id, 'email' => $user->email]);
$refresh_token = bin2hex(random_bytes(64));
$this->db->insert('user_tokens', [
'user_id' => $user->id,
'refresh_token' => $refresh_token,
'expires_at' => date('Y-m-d H:i:s', strtotime('+30 days'))
]);
return $this->response([
'status' => true,
'access_token' => $access_token,
'refresh_token' => $refresh_token,
], 200);
}
}
Step 4: Protect API Routes
Create a base controller file BaseApi_Controller.php
inside app/controllers
folder. Add the following code to base controller file.
class BaseApi_Controller extends REST_Controller
{
public $user_data;
public function __construct()
{
parent::__construct();
$this->load->library('Authorization_Token', null, 'authToken');
$user_data = $this->authToken->validateToken();
if (!$user_data) {
$this->response([
'status' => false,
'message' => 'Access token expired',
'token_expired' => true
], 401);
exit;
}
$this->user_data = $user_data;
}
}
This file handles token validations for all requests. But, it will not automatically intercept all requests. So, all your secure API files need to extend this BaseApi_Controller
.
class Orders extends Authenticated_Controller {
public function list_get() {
$user_id = $this->user_data['id'];
$orders = $this->db->get_where('orders', ['user_id' => $user_id])->result();
$this->output
->set_content_type('application/json')
->set_output(json_encode($orders));
}
}
Step 5: Token Refresh
Create new api file AuthController.php
for refresh token and add the following code in it.
class Auth extends CI_Controller {
public function refresh_token_post()
{
$refresh_token = $this->post('refresh_token');
$token_data = $this->db->get_where('user_tokens', [
'refresh_token' => $refresh_token
])->row();
if (!$token_data || strtotime($token_data->expires_at) < time()) {
return $this->response([
'status' => false,
'message' => 'Invalid or expired refresh token'
], REST_Controller::HTTP_UNAUTHORIZED);
}
// Generate new access token
$this->load->library('Authorization_Token', null, 'authToken');
$access_token = $this->authToken->generateToken([
'id' => $token_data->user_id,
'email' => 'user@email.com' // Fetch if needed
]);
return $this->response([
'status' => true,
'access_token' => $access_token,
'expires_in' => 900
], REST_Controller::HTTP_OK);
}
}
Summary
- JWT access tokens: short-lived (e.g., 15 minutes)
- Refresh tokens: long-lived (e.g., 30 days), stored securely
- On access token expiry: client uses refresh token to get a new one
REST_Controller
is used to simplify JSON responses in CodeIgniter 3
Final Thoughts
Implementing access and refresh tokens properly ensures secure and scalable mobile API sessions. Using CodeIgniter 3 with JWT and refresh tokens gives you full control over session lifecycle, security, and logout behavior.