CodeIgniter 4 Login and Register with JWT (JSON Web Token)

CodeIgniter 4 Login and Register with JWT (JSON Web Token)

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:

https://getcomposer.org/

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:

composer

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:

migrations

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:

spark serve

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:

register

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:

login

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:

me

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:

home

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:

token required

And if we access home with the wrong token, we will see the status: 401 Unauthorized and the message "Invalid Token" as shown below:

invalid token

 

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

Share:



Sponsorship:


Recommended for you


Comments (0)

Leave a Comment