Create a Secured RESTful API with CodeIgniter and JSON Web Tokens
Time to read: 10 minutes
The growing use and applications of cloud services necessitates a more efficient architectural style than the Simple Object Access Protocol (SOAP). REST (REpresentational State Transfer) allows for a light-weight, stateless communication between clients and the Application Programming Interface (API). Because the communication is stateless, access control for Restful APIs is based on tokens which carry enough information to determine whether or not the client is authorized to perform the requested action on the resource.
In this tutorial, I will use CodeIgniter to build a RESTful API. CodeIgniter is a powerful PHP framework with a very small footprint which allows developers to build full-scale web applications.
Prerequisites
A basic understanding of CodeIgniter will be helpful in this tutorial. However, I will provide explanations and links to official documentation throughout the tutorial. If you’re unclear on any concept, you can review the linked material before continuing with the tutorial.
Additionally, you need to have the following installed on your system:
- Composer. Composer will be used for dependency management in your CodeIgniter project
- A local database instance. While MySQL will be used in this tutorial, you are free to select your preferred database service
- Postman or a similar application to test our endpoints. You can also use cURL to test your endpoints
What We’ll Build
To demonstrate how to build a secure CodeIgniter API, we’ll build an API that will be used to manage the client database for a company. This database contains the following data on each client:
- Name
- Amount paid to retain the company’s services (Retainer fee)
The API built at the end of this tutorial will have the following functionalities:
- Register a new user
- Authenticate an existing user
- Add a new client
- Edit the details of an existing client
- View all clients
- View a single client by ID
- Delete a single client by ID
Features 3 to 7 will be restricted to authenticated users.
Getting Started
Create a new CodeIgniter project using Composer.
This will create a new CodeIgniter project within a folder named ci-secure-api. Once the installation is completed, move into the newly created project folder from the terminal and run the application on the local development server that comes installed with CodeIgniter. Use the following command to achieve that:
Navigate to http://localhost:8080/ from your browser to view the welcome page.
Environment Variables Preparation
Now that CodeIgniter is installed and running, the next step is to provide environment variables that will be used by our application. Stop the application from running by pressing CTRL + C
on the keyboard and proceed to make a copy of the .env file named .env using the command below:
CodeIgniter starts up in production mode by default. But for the sake of this tutorial, we will change it to development. To achieve that, uncomment the line shown below and set it to development:
Next, create a database within your local environment and uncomment the following variables to update each values to set up a successful connection to the database:
Replace the YOUR_DATABASE
, YOUR_DATABASE_USERNAME
, and YOUR_DATABASE_PASSWORD
placeholders with your own values.
Migrations and seeders
Now that we have created a database and set up a connection to it, we will create migrations for both the user
and client
table. Migration files are generally useful for creating a proper database structure. The migrations and seeders will be created using the CodeIgniter CLI tool.
Issue the following command in terminal:
The CLI will ask you to name the migration file after which it will create the migration file in the App/Database/Migrations directory. For this tutorial, you will create two migration files named:
add_client
add_user
The migration file name will be prefixed with a numeric sequence in the date format of YYYY-MM-DD-HHIISS
. Please see the CodeIgniter documentation for a more detailed explanation.
Next, update the content of the add_client
migration file as follows:
Here, we specified the fields and their corresponding data types for the Client
table.
Next, open the add_user
migration file and replace its content with the following:
The content above will help create the user table and its fields. Now run your migrations using the command below:
To make development easier, seed your database with some dummy client data. The fzaninotto faker bundle is a default dependency in the CodeIgniter skeleton and this can be used to add random clients to the database. Just as was done for the migration, the CodeIgniter CLI Tool will be used to create a seeder for clients. Run the following command:
The CLI will ask for a name called the ClientSeeder
. A ClientSeeder.php file will be created in the App/Database/Seeds directory. Open the file and replace its contents with the following:
Seed the database with dummy clients using the following command:
At this point, the database should have a similar structure to the screenshot below:
Entity Models
For the API’s interaction with the database, CodeIgniter’s Model will be used. For this to work, two models will be created - one for the User and another for the Client.
Open the App/Models directory and create the following files:
- UserModel.php
- ClientModel.php
In UserModel.php, add the following:
The beforeInsert
and beforeUpdate
functions allow you to perform an operation on the User entity before saving it to the database. In this case, the user’s password is hashed before it is saved to the database.
Add the following code to the ClientModel.php file:
The $table
field lets the Model know which database table it works with primarily. $allowedFields
lets the Model know which columns in the table can be updated. The findClientById
function provides a clean abstraction to retrieve a client from the database based on the id
provided.
With the Models and database implemented, users can be added and authenticated. Authorized users can also interact with current clientele.
JWT Implementation
JSON Web Tokens will be used to authenticate users and prevent unauthorized users from viewing the list of clients. For this to work, the API provides a token when the user registers or logs in successfully. This token will be appended to the header of subsequent requests to ensure that the API can identify the user making a request. In this tutorial, the firebase/php-jwt bundle will be used to generate the tokens. Run the following to install it using composer:
Once the installation is complete, add the following to your .env file:
Next, create a helper function to get the secret key in the Services class. Go to App/Config/Services.php and add the following:
Create JWT Helper
To help with the generation and verification of tokens, a Helper file will be created. This allows us to separate concerns in our application. In the App/Helpers directory create a file name jwt_helper.php. Your helper file should look like this:
The getJWTFromRequest
function checks the Authorization header of the incoming request and returns the token value. If the header is missing, it throws an exception which in turn causes an HTTP_UNAUTHORIZED
(401) response to be returned.
The validateJWTFromRequest
function takes the token obtained by the getJWTFromRequest
function. It decodes this token to get the email that the key was generated for. It then tries to find a user with that email address in the database. If the user was not found, the User Model throws an exception which is caught and returned to the user as an HTTP_UNAUTHORIZED
(401) response.
The getSignedJWTForUser
function is used to generate a token for an authenticated user. The encoded JWT contains the following details:
- The email of the authenticated user. This is used in subsequent requests to validate the source of the request.
- The time when the token was generated (
iat
). - The time when the token expires (exp). This is gotten by adding the.
JWT_TIME_TO_LIVE
value from our .env file to the current time.
Create Authentication Filter
In the App/Filters directory create a file named JWTAuthenticationFilter.php . This filter will allow the API check for the JWT before passing the request to the controller. If no JWT is provided or the provided JWT is expired, an HTTP_UNAUTHORIZED
(401) response is returned by the API with an appropriate error message. Add the following to your file:
As you can see, the JWT Helper is first loaded, then the getJWTFromRequest
and validateJWTFromRequest functions are used to ensure that the request is from an authenticated user with a valid token.
Register your JWTAuthentication filter and specify the route you want it to protect. This is done in the App/Config/Filters.php file. Update the $aliases
and $filters
array as follows:
NOTE: The Debug toolbar will be preloaded by default. There are known conflicts as the DebugToolbar is still under construction. To disable it, comment out the 'toolbar'
item in the $globals
array.
By adding these, the before
function in JWTAuthenticationFilter.php will be called anytime a request is sent to an endpoint starting with the client. This means that the controller will only receive/handle the request if a valid token is present in the request header.
Even though we don’t have any controller, we can check to see that our application is working so far. Open Postman and make a GET request to http://localhost:8080/client. You should see something similar to the screenshot below:
Next, open the App/Controllers/BaseController.php file and add the following function:
This function will be used by your controllers to return JSON responses to the client.
NOTE: Don’t forget to import the ResponseInterface.
BaseController
extends the CodeIgniter Controller
which provides helpers and other functions that make handling incoming requests easier. One of such functions is a validate
function which uses CodeIgniter’s validation service to check a request against rules (and error messages where necessary) specified in our controller functions. This function works well with form requests (form-data using Postman). However this would be unable to validate raw JSON requests sent to our API. This is because the content of the JSON request is stored in the body
field of the request while the content of the form-data request is stored in the post
field of the request.
To get around this, we’ll write a function that checks both fields in a request to get its content. Add the following to App/Controllers/BaseController.php:
NOTE: Don’t forget to import the IncomingRequest class.
Next, declare a function that runs the validation service against the $input
from our previous function. This function is almost the same as the inbuilt validate
function except that instead of running the check against the IncomingRequest
, we run it against the input we captured from the getRequestInput
function.
NOTE: Don’t forget to import the necessary classes.
With this in place, let’s add the logic to register and authenticate users.
Authentication Controller
Next, create a file name Auth.php in the App/Controllers directory. Update the file as shown below:
Registration
To successfully register a new user the following fields are required:
- A name.
- An email address in a valid format that is not less than 8 characters and not more than 255 characters.
- A password that is not less than 8 characters and not more than 255 characters.
The incoming request is checked against the specified rules. Invalid requests are discarded with an HTTP_BAD_REQUEST
code (400) and an error message. If the request is valid, the user data is saved and a token is returned along with the user’s saved details (excluding the password). The HTTP_CREATED
(201) response lets the client know that a new resource has been created.
Making a POST request to the register endpoint (http://localhost:8080/auth/register) with a valid name
, email address
and password
will result in a similar response to the one shown below:
Authentication
Successful authentication requires the following:
- An email address in a valid format that is not less than 8 characters and not more than 255 characters. Additionally the email address must correspond to that of a saved user.
- A password is not less than 8 characters and not more than 255 characters. As with the email address, the hash of the provided password must match the stored password hash associated with the provided email address.
However, doing the same for the login endpoint (http://localhost:8080/auth/login) would cause an Internal Server Error (HTTP Code 500). The reason for this is that we make use of a validateUser
function in our validation rules which we have not created yet.
User Validation
Create a new directory called Validation in the app directory. Inside of the app/Validation folder, create a file named UserRules.php and add the following code to the file:
Next, open the App/Config/Validation.php file and modify the $ruleSets
array to include your UserRules. $ruleSets
should look like this:
With the custom validation rules in place, the authentication request works as expected. Test this sending a POST HTTP request to thehttp://localhost:8080/auth/login endpoint with the details of the user-created earlier:
Create Client Controller
For the client controller, we will specify the routes in the app/Config/Routes.php file. Open the file and add the following routes:
By doing this, your API is able to handle requests with the same endpoint but different HTTP verbs accordingly.
Next, in the App/Controllers directory, create a file called Client.php. The contents of the file should be as follows:
The index
, store,
and show
functions are used to handle requests to view all clients, add a new client, and show a single client respectively.
Next, create two functions update
and destroy
. The update
function will be used to handle requests to edit a client. None of the fields are required hence any expected value that isn’t provided in the request is removed before updating the client in the database. The destroy
function will handle requests to delete a particular client.
With this in place, our API is set for consumption. Restart your application and test it by sending requests (via Postman, cURL, or your preferred application)
Add Access Token
Once you are done with the registration and login process, copy the value of the access_token
from the response. Next, click on the Authorization
tab and select Bearer Token
from the dropdown and paste the value of the access_token
copied earlier:
Create New Client
To create a new client, send a POST
HTTP request to http://localhost:8080/client:
Get All Clients
To fetch the list of Clients created so far, send a GET
HTTP request to http://localhost:8080/client:
Get Client by ID
Retrieve the details of a particular client by sending a GET
HTTP request to http://localhost:8080/client/1. In this case, 1
was used to specify the unique id
of the client that needs to be fetched from the database:
Conclusion
In this article, we create a PHP-based API using CodeIgniter. This allowed the execution of basic CRUD (Create, Read, Update, Delete) operations on a resource (Client). Additionally, we added a layer of security by restricting access to the resource. We also learned how to structure our project in a manner that separates concerns and makes our application loosely coupled.
The entire codebase for this tutorial is available on GitHub. Feel free to explore further. Happy coding!
Oluyemi is a tech enthusiast with a background in Telecommunication Engineering. With a keen interest to solve day to day problems encountered by users, he ventured into programming and has since directed his problem solving skills at building softwares for both web and mobile. A full stack software engineer with a passion for sharing knowledge, Oluyemi has published a good number of technical articles and content on several blogs on the internet. Being tech savvy, his hobbies include trying out new programming languages and frameworks.
- Twitter: https://twitter.com/yemiwebby
- GitHub: https://github.com/yemiwebby
- Website: https://yemiwebby.com.ng/
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.