Que vamos ver en este artículo
1) Crear proyecto de codeigniter, requerimientos y configuraciones
En este punto vamos a:- Crear el proyecto
- Instalar el componente JWT dentro del proyecto
- Crear la base de datos de SQLite
- Configurar el fichero .env
- Crear la tabla de usuarios con una migración
composer create-project codeigniter4/appstarter ci-api-ejemplo
Y seguidamente dentro del directorio del proyecto instalamos la dependencia de JWT y así ya lo tenemos todo listo
cd ci-api-ejemplo
composer require firebase/php-jwt
touch writable/database.db
Damos permisos al directorio writable/
sudo chown 775 writable/ -R
sudo chown user:www-data writable/ -R
Configuramos el fichero .env, que no existe, hay que renombrar el que viene env por .env y dentro poner la conexión a la base de datos
# Configuración de la base de datos para SQLite
database.default.DBDriver = SQLite3
database.default.database = /ruta/completa/a/writable/database.db
database.default.DBPrefix =
database.default.charset = utf8
database.default.DBDebug = true
Y ya que estamos el entorno
CI_ENVIRONMENT = development
Y el token JWT que utilizaremos
JWT_SECRET=t4DTq3wKW6EfnHb9x2kjumBN58JdFV7P
Así el fichero .env ya lo tenemos listo.
Para probar si hemos creado bien la base de datos y la configuración a la base de datos se puede ejecutar el siguiente comando
php spark db:table
Este comando devuelve el listado de tablas o un mensaje que no hay tablas si la base de datos todavía está vacía- Si devuelve un error, efectivamente es que hay un error en la configuración. Ahora vamos a crear la tabla de usuarios Para esto crearemos una migración de la siguiente manera:php spark make:migration CreateUsersTable
Y con el editor, por ejemplo, Visual Studio Code, abrimos el fichero que ha creado en APPPATH/Database/Migrations/2025-03-20-111032_CreateUsersTable.phpY ponemos el siguiente código
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateUsersTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'auto_increment' => true, 'unsigned' => true],
'username' => ['type' => 'VARCHAR', 'constraint' => 100],
'email' => ['type' => 'VARCHAR', 'constraint' => 150, 'unique' => true],
'password' => ['type' => 'VARCHAR', 'constraint' => 255],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true]
]);
$this->forge->addPrimaryKey('id');
$this->forge->createTable('users');
}
public function down()
{
$this->forge->dropTable('users');
}
}
y ejecutamos la migración
php spark migrate
Y si miramos la base de datos tendríamos que tener la tabla users creada
1) Crear el Modelo de usuario y el controlador de autenticación y las rutas
Ya lo tenemos configurado el proyecto así que ahora crearemos- Un modelo para usuarios
- Un controlador para autenticación
- Y configuraremos el fichero de rutas
php spark make:model UserModel
Crea un fichero en APPPATH/Models/UserModel.php,Aquí solo hay que añadir al array de allowedFields los campos de la tabla users y quedará de esta forma
protected $allowedFields = ['username', 'email', 'password'];
Vamos con el controller de autenticación, lo creamos desde el terminal también
php spark make:controller AuthController
Crea el controlador en APPPATH/Controllers/AuthController.php, lo abrimos y eliminar lo que hay dentro y copiar el siguiente código
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\RESTful\ResourceController;
use App\Models\UserModel;
use CodeIgniter\API\ResponseTrait;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use App\Libraries\JWTAuthService;
class AuthController extends ResourceController
{
use ResponseTrait;
public function register()
{
$rules = [
'email' => 'required|valid_email|is_unique[users.email]',
'username' => 'required|min_length[3]|is_unique[users.username]',
'password' => 'required|min_length[8]',
];
if (!$this->validate($rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$data = [
'email' => $this->request->getVar('email'),
'username' => $this->request->getVar('username'),
'password' => password_hash($this->request->getVar('password'), PASSWORD_BCRYPT),
];
$userModel = new UserModel();
$userModel->save($data);
return $this->respondCreated([
'status' => 201,
'message' => 'Usuario registrado correctamente',
'user_id' => $userModel->getInsertID()
]);
}
public function login()
{
$rules = [
'email' => 'required|valid_email',
'password' => 'required'
];
if (!$this->validate($rules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$userModel = new UserModel();
$user = $userModel->where('email', $this->request->getVar('email'))->first();
if (!$user) {
return $this->failNotFound('Email no encontrado');
}
$verify = password_verify($this->request->getVar('password'), $user['password']);
if (!$verify) {
return $this->fail('Contraseña incorrecta', 401);
}
$key = getenv('JWT_SECRET');
$payload = [
'iat' => time(),
'exp' => time() + 3600, // Token válido por 1 hora
'uid' => $user['id'],
];
$token = JWT::encode($payload, $key, 'HS256');
return $this->respond([
'status' => 200,
'message' => 'Login correcto',
'token' => $token,
'user' => [
'id' => $user['id'],
'email' => $user['email'],
'username' => $user['username']
]
]);
}
public function profile()
{
# Obtener ID de usuario del servicio de autenticación
$userId = JWTAuthService::getInstance()->getUserId();
$userModel = new UserModel();
$user = $userModel->find($userId);
if (!$user)
{
return $this->failNotFound('Usuario no encontrado');
}
return $this->respond([
'status' => 200,
'user' => [
'id' => $user['id'],
'email' => $user['email'],
'username' => $user['username']
]
]);
}
}
Aquí tenemos los métodos de registro, login y profile.
Ahora creamos las rutas
Editar el fichero config/Routes.php y copiamos el siguiente código
$routes->group('api', ['namespace' => 'App\Controllers'], function ($routes)
{
$routes->post('register', 'AuthController::register');
$routes->post('login', 'AuthController::login');
// Rutas protegidas que requieren autenticación
$routes->group('', ['filter' => 'jwt'], function ($routes)
{
$routes->get('profile', 'AuthController::profile');
// Aquí puedes agregar otras rutas protegidas
});
});
Ahora queda crear el filtro JWT que aplicamos en estas rutas protegidas
$routes->group('', ['filter' => 'jwt'],
y el servicio para guardar los tokens1) Añadir un filtro para validación del token JWT, un servicio singleton y un helper
Lo primero el filtro, los filtros en codeigniter son como los Middleware, se ejecutan antes y/o después del controllerPara crear un filtro ejecutamos desde el terminal
php spark make:filter JWTAuthFilter
Y crea un fichero en APPPATH/Filters/JWTAuthFilter.phpLo abrimos y copiamos lo siguiente dentro
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Firebase\JWT\JWT;
use Firebase\JWT\ExpiredException;
use Config\Services;
use Firebase\JWT\Key;
use App\Libraries\JWTAuthService;
class JWTAuthFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$header = $request->getServer('HTTP_AUTHORIZATION');
if (!$header)
{
return Services::response()
->setJSON([
'status' => 401,
'error' => 'Token de autenticación no proporcionado'
])
->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED);
}
try
{
$token = explode(' ', $header)[1];
$key = getenv('JWT_SECRET');
$decoded = JWT::decode($token, new Key($key, 'HS256'));
# Almacenar la información del token en el servicio singleton
JWTAuthService::getInstance()->setData($decoded, $token);
return $request;
}
catch (ExpiredException $e)
{
return Services::response()
->setJSON([
'status' => 401,
'error' => 'Token expirado'
])
->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED);
}
catch (\Exception $e)
{
return Services::response()
->setJSON([
'status' => 401,
'error' => 'Token inválido: ' . $e->getMessage()
])
->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED);
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// No se necesita hacer nada después
}
}
Para que el filtro funcione correctamente hay que añadirlo al fichero de configuración app/Config/Filters.php
public array $aliases = [
// Otros filtros
'jwt' => \App\Filters\JWTAuthFilter::class
];
El que está al final del todo jwt
Y ahora creamos el servicio para guardar los tokens cuando se validan y en el controlador cuando se accede a una url protegida
Creamos el siguiente fichero app/Libraries/JWTAuthService.php y dentro ponemos el siguiente código
namespace App\Libraries;
class JWTAuthService
{
private static $instance = null;
private $userData = null;
private $token = null;
private function __construct() {}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function setData($userData, $token)
{
$this->userData = $userData;
$this->token = $token;
return $this;
}
public function getUserId()
{
return isset($this->userData->uid) ? $this->userData->uid : null;
}
public function getToken()
{
return $this->token;
}
public function getUserData()
{
return $this->userData;
}
public function clear()
{
$this->userData = null;
$this->token = null;
return $this;
}
}
El servicio con singleton para almacenar los tokens esta bien para una implementación estandar de PHP pero en sistemas de alto rendimiento consideraria la opción de utilizar Redis, dicho esto ...
Cómo funciona este proceso de guardar el token
Cuando se accede a una url protegida con el filtro JWTAuthFilter después de decodificar el token y comprobar que es correcto con esta línea se almacena.
# Almacenar la información del token en el servicio singleton
JWTAuthService::getInstance()->setData($decoded, $token);
Después en el controlador, en el ejemplo en el método profile se obtiene el id del usuario para buscarlo en la bbdd
# Almacenar la información del token en el servicio singleton
$userId = JWTAuthService::getInstance()->getUserId();
En otros métodos utilizaremos otros datos del token
1) Probar la API
Para probar la API podemos utilizar cualquiera de los clientes de API Rest que existen postman, Thunder Client (extensión para visual studio code) o el terminal por curlRegistro de un usuario, enviaremos los siguiente:
Un POST a http://localhost/api/register con los siguientes datos
{
"email": "usuario@ejemplo.com",
"username": "usuario",
"password": "Asdf1234"
}
Y tiene que devolver como salida
{
"status": 201,
"message": "Usuario registrado correctamente",
"user_id": 1
}

Login de usuario, enviaremos
Un POST a http://localhost/api/login con los siguientes datos
{
"email": "usuario@ejemplo.com",
"password": "Asdf1234"
}
Y devuelve lo siguiente
{
"status": 200,
"message": "Login correcto",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3NDI0ODQ4NTYsImV4cCI6MTc0MjQ4ODQ1NiwidWlkIjoxfQ.0Y6vPXLW-eijtQHS6LQe6vv2AoRuD-5iUFAMrk4zD80",
"user": {
"id": 1,
"email": "usuario@ejemplo.com",
"username": "usuario"
}
}
Dentro de estos datos que devuelve el login necesitaremos el token para enviarlo con las peticiones que necesitas de validación

Ver el profile de usuario Un GET a http://localhost/api/profile y en el apartado de Auth->Bearer ponemos el token obtenido en la llamada al login

Y ya tenemos la API funcionando, ahora solo queda ir poniendo funcionalidades.
1) Conclusión
Como hemos visto crear una API con Codeigniter es muy sencillo y rápido.Es una versión sencilla donde se pueden ir añadiendo mejoras como la del redis que comentaba y nuevas funcionalidad
Todo el código del proyecto está en el repositorio de github
Y esto es todo, feliz programming!!!
Saludos
Alex
:-)
/