In this tutorial you will learn how to create a login, register, and protects endpoints or resources with JSON Web Token using CodeIgniter 4.
(Step-by-Step)
Let's get started.
Step #1. CodeIgniter 4 Installation
To install CodeIgniter 4 can be done in 2 ways, namely: Manual installation and Installation using composer.
In this tutorial, I will use composer.
If you don't have composer, please visit the following URL to download composer, then install it on your computer:
After composer is installed on your computer and to make sure composer is installed properly on your computer, open a terminal or Command Prompt (CMD).
Then type the following command:
composer -v
Like the following picture:
Once composer is properly installed on your computer, then you can create a CodeIgniter 4 project using composer.
Create a new folder on your web server, here I name it “jwt-app”.
If you are using WAMPSERVER, create it in folder:
C:/wamp64/www
If you are using XAMPP, create it in the folder:
C:/xampp/htdocs
In this tutorial, I'm using XAMPP.
Then open the "jwt-app" folder using the Code Editor, here I use Visual Studio Code.
After that, integrate it with the terminal on Visual Studio Code.
Then type the following command in the terminal to create a CodeIgniter 4 project:
composer create-project codeigniter4/appstarter .
The above command will create a new CodeIgniter project in the “jwt-app” folder.
Wait until the installation process is complete.
Step #2. Create a Database
Create a new database in MySQL, you can use tools such as SQLyog, PHPMyAdmin or similar tools.
Here I create a database with the name "jsonwebtoken".
If you create a database with the same name it's even better.
To create a database in MySQL, it can be done by executing the following query:
CREATE DATABASE jsonwebtoken;
The SQL command above will create a database with the name "jsonwebtoken".
Next, create a connection between the database and the CodeIgniter project.
Find the env file in the project root, then rename it to .env and open the file.
Then find the following code:
# database.default.hostname = localhost # database.default.database = ci4 # database.default.username = root # database.default.password = root # database.default.DBDriver = MySQLi
Change it to like the following:
database.default.hostname = localhost database.default.database = jsonwebtoken database.default.username = root database.default.password = database.default.DBDriver = MySQLi
Step #3. Migrations
In this tutorial, I will use the migration feature in CodeIgniter 4 to create a table.
Type the following command in Terminal/Command Prompt:
php spark make:migration Users
Press Enter, then CodeIgniter will create a file with the initials "Users" in the "app/Database/Migrations" folder.
Like the following picture:
Open the file, then type the following code:
<?php namespace App\Database\Migrations; use CodeIgniter\Database\Migration; class Users extends Migration { public function up() { $this->forge->addField([ 'id' => [ 'type' => 'INT', 'constraint' => 5, 'auto_increment' => true ], 'email' => [ 'type' => 'VARCHAR', 'constraint' => 100 ], 'password' => [ 'type' => 'VARCHAR', 'constraint' => 200 ] ]); $this->forge->addKey('id', true); $this->forge->createTable('users'); } public function down() { // } }
In the code above, we only add code to the function up().
Function up, serves to create a table in the database with the specified fields.
In the above case, we create a table with the name “users” with fields id, email, and password along with the data type and other attributes.
If you're not familiar with migrations, it may look complicated, but it's really not.
Next, type the following command at the Terminal/Command Prompt:
php spark migrate
Press Enter, then CodeIgniter will automatically create a “users” table with fields: id, email, and password in the database “jsonwebtoken”.
Step #4. JSON Web Token Installation
To install JSON Web Token for PHP, run the following command in terminal:
composer require firebase/php-jwt
Then composer will install JSON Web Token automatically in your project.
Once the installation is completed. Go to the “.env” file, then add the variable secret key as follows:
TOKEN_SECRET = cskhfuwt48wbfjn3i4utnjf38754hf3yfbjc93758thrjsnf83hcwn8437
You are free to create the value of secret key, the more complicated the better.
Step #5. Model
Create a model file by typing the following command in the terminal:
php spark make:model UserModel
Then CodeIgniter will automatically create a model file named "UserModel.php" in the "app/Models" folder.
Then, open the file and change the code to be like this:
<?php namespace App\Models; use CodeIgniter\Model; class UserModel 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 = ['email','password']; // Dates protected $useTimestamps = false; protected $dateFormat = 'datetime'; protected $createdField = 'created_at'; protected $updatedField = 'updated_at'; protected $deletedField = 'deleted_at'; // Validation protected $validationRules = []; protected $validationMessages = []; 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 = []; }
In the code above, we only make a few changes to allowedFields.
Step #6. Controllers
Create a controller file by typing the following command in the terminal:
php spark make:controller Register --restful
Then CodeIgniter will automatically create a controller file named "Register.php" in the "app/Controllers" folder.
Then, open the file and change the code to be like this:
<?php namespace App\Controllers; use CodeIgniter\RESTful\ResourceController; use CodeIgniter\API\ResponseTrait; use App\Models\UserModel; class Register extends ResourceController { /** * Return an array of resource objects, themselves in array format * * @return mixed */ use ResponseTrait; public function index() { helper(['form']); $rules = [ 'email' => 'required|valid_email|is_unique[users.email]', 'password' => 'required|min_length[6]', 'confpassword' => 'matches[password]' ]; if(!$this->validate($rules)) return $this->fail($this->validator->getErrors()); $data = [ 'email' => $this->request->getVar('email'), 'password' => password_hash($this->request->getVar('password'), PASSWORD_BCRYPT) ]; $model = new UserModel(); $registered = $model->save($data); $this->respondCreated($registered); } }
The controller is used for registration of new users.
Next, create a controller file to login by running the following command on the terminal:
php spark make:controller Login --restful
Then CodeIgniter will automatically create a controller file named "Login.php" in the "app/Controllers" folder.
Then, open the Login.php file and change the code to be like this:
<?php namespace App\Controllers; use CodeIgniter\RESTful\ResourceController; use CodeIgniter\API\ResponseTrait; use App\Models\UserModel; use Firebase\JWT\JWT; class Login extends ResourceController { /** * Return an array of resource objects, themselves in array format * * @return mixed */ use ResponseTrait; public function index() { helper(['form']); $rules = [ 'email' => 'required|valid_email', 'password' => 'required|min_length[6]' ]; if(!$this->validate($rules)) return $this->fail($this->validator->getErrors()); $model = new UserModel(); $user = $model->where("email", $this->request->getVar('email'))->first(); if(!$user) return $this->failNotFound('Email Not Found'); $verify = password_verify($this->request->getVar('password'), $user['password']); if(!$verify) return $this->fail('Wrong Password'); $key = getenv('TOKEN_SECRET'); $payload = array( "iat" => 1356999524, "nbf" => 1357000000, "uid" => $user['id'], "email" => $user['email'] ); $token = JWT::encode($payload, $key); return $this->respond($token); } }
Then, create a controller file again to get the current user login by running the following command in the terminal:
php spark make:controller Me --restful
Then CodeIgniter will automatically create a controller file named "Me.php" in the "app/Controllers" folder.
Then, open the Me.php file and change the code to be like this:
<?php namespace App\Controllers; use CodeIgniter\RESTful\ResourceController; use CodeIgniter\API\ResponseTrait; use App\Models\UserModel; use Firebase\JWT\JWT; class Me extends ResourceController { /** * Return an array of resource objects, themselves in array format * * @return mixed */ use ResponseTrait; public function index() { $key = getenv('TOKEN_SECRET'); $header = $this->request->getServer('HTTP_AUTHORIZATION'); if(!$header) return $this->failUnauthorized('Token Required'); $token = explode(' ', $header)[1]; try { $decoded = JWT::decode($token, $key, ['HS256']); $response = [ 'id' => $decoded->uid, 'email' => $decoded->email ]; return $this->respond($response); } catch (\Throwable $th) { return $this->fail('Invalid Token'); } } }
Step #7. Filters
Create a filter file that serves to protect end-points from non-authenticated users.
To create a filter file, run the following command on the terminal:
php spark make:filter Auth
Then CodeIgniter will automatically create a filter file named "Auth.php" in the "app/Filters" folder.
Then, open the Auth.php file and change the code to be like this:
<?php namespace App\Filters; use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Firebase\JWT\JWT; use Config\Services; class Auth implements FilterInterface { /** * Do whatever processing this filter needs to do. * By default it should not return anything during * normal execution. However, when an abnormal state * is found, it should return an instance of * CodeIgniterHTTPResponse. If it does, script * execution will end and that Response will be * sent back to the client, allowing for error pages, * redirects, etc. * * @param RequestInterface $request * @param array|null $arguments * * @return mixed */ public function before(RequestInterface $request, $arguments = null) { $key = getenv('TOKEN_SECRET'); $header = $request->getServer('HTTP_AUTHORIZATION'); if(!$header) return Services::response() ->setJSON(['msg' => 'Token Required']) ->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED); $token = explode(' ', $header)[1]; try { JWT::decode($token, $key, ['HS256']); } catch (\Throwable $th) { return Services::response() ->setJSON(['msg' => 'Invalid Token']) ->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED); } } /** * Allows After filters to inspect and modify the response * object as needed. This method does not allow any way * to stop execution of other after filters, short of * throwing an Exception or Error. * * @param RequestInterface $request * @param ResponseInterface $response * @param array|null $arguments * * @return mixed */ public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { // } }
Then open the "Filters.php" file located in the "app/Config" folder.
Then find the following code:
public $aliases = [ 'csrf' => CSRF::class, 'toolbar' => DebugToolbar::class, 'honeypot' => Honeypot::class ];
Then change it to be like this:
public $aliases = [ 'csrf' => CSRF::class, 'toolbar' => DebugToolbar::class, 'honeypot' => Honeypot::class, 'auth' => App\Filters\Auth::class ];
Next, do a little configuration on the "Routes.php" file located in the "app/Config" folder.
Find the following code:
$routes->get('/', 'Home::index');
Then change it to be like this:
$routes->get('/', 'Home::index', ['filter' => 'auth']); $routes->post('register', 'Register::index'); $routes->post('login', 'Login::index');
In the above code, we protect the home endpoint with an "auth" filter.
In other words, we cannot access the home controller without the access token.
If you have multiple endpoints that you want to protect, you just need to add an “auth” filter to the routes like the example above.
Next, create another filter file for CORS (Cross-Origin Resource Sharing) so that our API can be accessed from outside the domain.
To create a CORS filter, run the following command in the terminal:
php spark make:filter Cors
Then CodeIgniter will automatically create a filter file named "Cors.php" in the "app/Filters" folder.
Then, open the Cors.php file and change the code to be like this:
<?php namespace App\Filters; use CodeIgniter\Filters\FilterInterface; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; class Cors implements FilterInterface { /** * Do whatever processing this filter needs to do. * By default it should not return anything during * normal execution. However, when an abnormal state * is found, it should return an instance of * CodeIgniterHTTPResponse. If it does, script * execution will end and that Response will be * sent back to the client, allowing for error pages, * redirects, etc. * * @param RequestInterface $request * @param array|null $arguments * * @return mixed */ public function before(RequestInterface $request, $arguments = null) { header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Headers: X-API-KEY, Origin,X-Requested-With, Content-Type, Accept, Access-Control-Requested-Method, Authorization"); header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PATCH, PUT, DELETE"); $method = $_SERVER['REQUEST_METHOD']; if($method == "OPTIONS"){ die(); } } /** * Allows After filters to inspect and modify the response * object as needed. This method does not allow any way * to stop execution of other after filters, short of * throwing an Exception or Error. * * @param RequestInterface $request * @param ResponseInterface $response * @param array|null $arguments * * @return mixed */ public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { // } }
Open the "Filters.php" file located in the "app/Config" folder, then add the “cors” filter as follows:
public $aliases = [ 'csrf' => CSRF::class, 'toolbar' => DebugToolbar::class, 'honeypot' => Honeypot::class, 'auth' => App\Filters\Auth::class, 'cors' => App\Filters\Cors::class, ];
Next define "cors" in public $globals as follows:
public $globals = [ 'before' => [ // 'honeypot', // 'csrf', 'cors' ], 'after' => [ //'toolbar', // 'honeypot', ], ];
Step #8. Testing
To do the test, run our project by typing the following command in the terminal:
php spark serve
Like the following picture:
In this tutorial I will use POSTMAN to test.
I also recommend you to use POSTMAN.
You can download POSTMAN at the following link, and install it on your computer:
https://www.postman.com/downloads/
Once POSTMAN is installed on your computer, open POSTMAN for testing.
#1. Register
Type the following URL in the Postman URL field:
http://localhost:8080/register
Select the POST method => Body => raw => JSON => enter the data in JSON format, then click the Send button, as shown below:
If it goes well, you will see the status: 201 Created as shown above.
#2. Login
Type the following URL in the Postman URL field:
http://localhost:8080/login
Select the POST method => Body => raw => JSON => enter the email and password data in JSON format, then click the Send button, as shown below:
If the login is successful, then we will get a token response like the picture above.
We can use the token to access protected resources.
#3. Get Current User Login
Type the following URL in the Postman URL field:
http://localhost:8080/me
Select the GET method => Authorization => Bearer Token => copy and paste the previous token in the token field, then click the Send button, as shown below:
If it goes well, then we get the “id” and “email” from the logged in user like the picture above.
#4. Access Protected Resources
We can also access protected resources by using the token.
In this tutorial, we protect the home route. In other words, we can't access home without token.
Type the following URL in the Postman URL field:
http://localhost:8080
Select the GET method => Authorization => Bearer Token => copy and paste the previous token in the token field, then click the Send button, as shown below:
If it goes well, it will show the status: 200 Ok and the html response is like the picture above.
If we access the home without a token, we will see the status: 401 Unauthorized and the message "Token Required" as shown below:
And if we access home with the wrong token, we will see the status: 401 Unauthorized and the message "Invalid Token" as shown below:
Conclusion:
The discussion this time is about how to create login, register, and protect endpoints or resources with JSON Web Tokens using CodeIgniter 4.
So what are you waiting for, let's coding!
Download Source Code
Comments (0)