JWT-AUTH Exception Handling In Laravel 8
Tags: laravel, php, jwt
Published: 2021-04-26 14:12:19

Are you experiencing difficulties with handling JWT-AUTH Exceptions in Laravel 8 or you're curious to know how it works? Throughout this article, I will be guiding you through an easy process of understanding it.

### INTRODUCTION

__JWT-AUTH -> (JSON Web Token Authentication For Laravel and Lumen).__

JWT is mainly used for authentication. After a user logs in to an application, the application will create a JWT and send it back to the user. Subsequent requests by the user will include the JWT. The token tells the server what routes, services, and resources the user is allowed to access

We will be creating a basic Register and Login API where authorized users can fetch their information from the database with JWT implemented and then handle some exceptions. Let's begin🤩, I hope you enjoy this guide.


__Step 1: Creating a new laravel project__
You can create a new laravel project with the following command:

```bash
laravel new jwt_exception_handling
```
__Step 2: Set up Model and Migrations for Users__
* We can set up models and migrations simultaneously like this:

```bash
php artisan make:model User -m
```
> PS: A user model and migration already exists because they come default with laravel in `App/Models` and `database/migrations` directory respectively.

* Set Up Database Connection

This is done in the `.env` file based on where you are serving your database like this:

> PS: These configurations are for my own local machine, yours may be different.

```php
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=jwt_handling
DB_USERNAME=root
DB_PASSWORD=
```
* Next is to set up the schema for our migration but since our `create_users_table.php` migration file comes default with laravel, we have nothing to do here.   

```php
public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
```
* Finally we can run our migrations to our database.

We run migrations to our database with this Artisan CLI command:

```bash
php artisan migrate
```

__Step 3: Set up Register method User Controller.__
* We  create controllers with this Artisan CLI command:

```bash
php artisan make:controller UserController
```
* Implement the User model in the UserController.
```php
use App\Models\User;
```

* Define the `register()` method in the UserController class.

The `register()` method validates a users' input and creates a user if the user credentials are validated.

```php
public function register(Request $request)
    {
        $this->validate($request, [
            'name'=>'required',
            'email'=>'required|email|unique:users',
            'password'=>'required'
        ]);

        $user = new User([
            'name'=> $request->input('name'),
            'email'=> $request->input('email'),
            'password'=> bcrypt($request->input('password'))
        ]);

        $user->save();

        return response()->json([
            'message'=>'Successfully Created user'
        ],201);
    }
```

* Next, we want to define the `login()` method but before that, we need to import JWT.

__Step 4: Import JWT-AUTH.__

* To pull in the latest version of jwt-auth, run this command:
```bash
composer require tymon/jwt-auth
```
* Add the service provider to the providers array and alias to the aliases array in the `config/app.php` config file like this:

```php
'providers' => [

    ...

    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
 'aliases' => [
        'JWTAuth'=>Tymon\JWTAuth\Facades\JWTAuth::class,
    ],
```
* Run the following command to publish the package config file:

```bash
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
```
You should now have a `config/jwt.php` file that allows you to configure the basics of this package.

* There is a helper command to generate a key that will be used to sign your tokens:

```bash
php artisan jwt:secret
```
This will update your `.env file` with something like JWT_SECRET = (key generated).

__Step 5: Set up JWT-AUTH in our project.__

* Update your User model

Firstly,we need to implement the `Tymon\JWTAuth\Contracts\JWTSubject` contract on your User model, which requires you implement 2 methods `getJWTIdentifier()` and `getJWTCustomClaims()`. 
Your User model should be updated like this:

```php
<?php

namespace App\Models;


use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }
}
```
* Next, we need to configure Auth Gaurd

You'll need to update the `config/auth.php` file with these changes to configure Laravel to use the `jwt` guard to power your application authentication. 
> PS: Update only these arrays and leave the others. 
```php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

...

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
```
__Step 6: Set Up Login Method in UserController__

* First we will implement JWT and its exception to our UserController.
```php
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
```
* Now back to our UserController class, we define the `login()` method for users to login.

This will validate users' input. The auth method accepts a request containing an email and password which will be checked against the user database for authentication.
Once you authenticate, the controller returns a JWT that you need to keep.

```php
public function login(Request $request)
    {
        $this->validate($request, [
            'email' => 'required|email',
            'password' => 'required'
        ]);

        $credentials = $request->only('email', 'password');
        try {
            if (!$token = JWTAuth::attempt($credentials)) {
                return response()->json([
                    'error' => 'Invalid Credentials'
                ], 401);
            }
        } catch (JWTException $e) {
            return response()->json([
                'error' => 'Could not create token'
            ], 500);
        }
        return response()->json([
            'token' => $token
        ], 200);
    }
```

__Step 7: Set up getUser Method in the UserController.__

Still, in our UserController, we define `getUser()` method which is responsible for getting users' personal information from the database.

```php
public function getUser(){
        $user = auth('api')->user();
        return response()->json(['user'=>$user], 201);
    }
```

__Step 8: Exception Handling__
* Firstly, we create a route middleware to parse the token of an authenticated user.
We make a middleware with this Artisan CLI command:

```bash
php artisan make:middleware JWTMiddleWare
```
A class of JWTMiddleware at`App/Http/Middleware/JWTMiddleware.php` directory will be created.

* Next, we implement JWTAuth in the JWTMiddleware.php like this:

```php
use JWTAuth;
```

* The middleware comes with a default method handle and now we parse the generated token of an authenticated user like this:

```php
 public function handle(Request $request, Closure $next)
    {

        $user = JWTAuth::parseToken()->authenticate();
        return $next($request);
    }
```
* Add the middleware to the array of routeMiddleware in the `kernel.php` like this: 

```php
protected $routeMiddleware = [
        .......
        'auth.jwt'=>\App\Http\Middleware\JwtMiddleWare::class,
    ];
```
* Exception Handling in Handlers Class.

We will create the handlers for our exception in `Handler.php` in `app/Exceptions` directory.
Our exceptions will be in the `register()` method that comes default with `Handler.php`.

We will be handling the following exceptions: Invalid token, Expired Token, JWTException but we need to implement these Exceptions in `Handler.php`.

```php
use Response;
use Exception;
use Throwable;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
```
Now, to the `register()` method:

```php
public function register()
    {
     
        $this->renderable(function(TokenInvalidException $e, $request){
                return Response::json(['error'=>'Invalid token'],401);
        });
        $this->renderable(function (TokenExpiredException $e, $request) {
            return Response::json(['error'=>'Token has Expired'],401);
        });

        $this->renderable(function (JWTException $e, $request) {
            return Response::json(['error'=>'Token not parsed'],401);
        });

    }
```

__Step 9: Set Up Routes.__

Seeing we're are creating an API, we will set up our routes in `routes/api.php`.

* We need to implement our UserController first.
```php
use  App\Http\Controllers\UserController;
```
* Then we set up our routes.

We will be adding the JWTMiddleware to the routes for getting users' details, this way we can see the work of Exception Handling.
```php
Route::post('/register', [
    UserController::class, 'register'
]);
Route::post('/login', [
    UserController::class, 'login'
]);
Route::get('/user', [
    UserController::class, 'getUser'
])->middleware('auth.jwt');

```
Congratulations, you have successfully built an API with JWT-AUTH😍, Now let us test it with postman.

__Step 10: Test with Postman.__

* This shows that our registration works with valid input.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bfkpv52r6yb903jr911g.png)

* This shows that our login works with valid input.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtzv9io6p77xm2vz6wzy.png)

* Now to get users' information, we need to pass the token alongside the request and we do this by setting Authorization to Bearer (Token) in our headers like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xw452z7cuhl8u0r5mltg.png)

**Let's verify if our Exception Handling works.**

For JWTException, we do not pass token with the request:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajxegej563k09hwwksle.png)

For TokenInvalidException, we put in the wrong token:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j30zl66cc6n97g3po050.png)

* For TokenExpiredException, we resend that token after a long period of time depending on the time to live for the token.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/txw3wrhs04kq91lgdyrk.png)

Congratulations on building an API and implementing JWT-AUTH and also handling some exceptions😍.

Guess what?😊 The code for this practice is open-source here on my [Github](https://github.com/Roxie-32/jwt_exception_handling).

These are my humble opinions so please, if you have any contradicting or buttressing opinions, do well to reach me on [Twitter](https://twitter.com/OlasupoFunke).

If you need more information on jwt-auth, visit the official [documentation](https://jwt-auth.readthedocs.io/en/develop/).

Thank you for reading till the end🤝🤩😍