How to implement JWT in Laravel API
Tags: laravel, security, php, jwt
Published: 2024-05-04 22:45:50

How to implement JWT Authentication.
Effortlessly Implementing it in Fast, Simple Steps.

After you have finished reading my post see my repo:  [jwt-auth-in-laravel](https://github.com/germanlozickyj/jwt-auth-in-laravel)

## Table Of Contents
 * [Install](#Install)
 * [Implementation](#Implementation)
 * [Postman](#Postman)

## Install <a name="Install"></a>
* Install package php-open-source-saver/jwt-auth

```
composer require php-open-source-saver/jwt-auth
```

* Run the following command to publish the package config file:

```
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
```

* I have included a helper command to generate a key for you:

```
php artisan jwt:secret
```
> This will update your .env file with something like JWT_SECRET=foobar

It is the key that will be used to sign your tokens. How that happens exactly will depend on the algorithm that you choose to use.


## Implementation <a name="Implementation"></a>


The implementation **depends** if you want to use algorithms of [symmetric](https://www.freecodecamp.org/news/encryption-explained-in-plain-english/) or [asymmetric](https://www.freecodecamp.org/news/encryption-explained-in-plain-english/) encryption


:warning: If you want to implement the JWT authentication with a symmetric algorithm please scroll to *code implementation* :warning:



### Generate a certificate for asymmetric encryption

> For generating certificates the command

```
php artisan jwt:generate-certs
```

> can be used. The .env file will be updated, to use the newly created certificates.

The command accepts the following parameters.


| name        | description                     |
|-------------|---------------------------------|
| force       | override existing certificates  |                
| algo	      | Either rsa or ec                |
| bits        | Key length for rsa              | 
| curve       | Curve to be used for ec         |    
| sha         |	Hashing algorithm               |
| passphrase  |	Passphrase for the cert         |
| dir         | Folder to place the certificates|

 





#### Examples
Generating a 4096-bit rsa certificate with sha 512

```
php artisan jwt:generate-certs --force --algo=rsa --bits=4096 --sha=512
```

Generating an ec certificate with prime256v1-curve and sha 512

```
php artisan jwt:generate-certs --force --algo=ec --curve=prime256v1 --sha=512
```

After you have chosen the encryption algorithms with PUBLIC AND PRIVATE KEY.
Your .env file has to look like this:

`
JWT_PRIVATE_KEY=file://../storage/certs/jwt-ec-4096-private.pem
JWT_PUBLIC_KEY=file://../storage/certs/jwt-ec-4096-public.pem
`




## code implementation


* **First** You must add in config/auth.php the guard of JWT 



``` php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
```

* Your User model has to look like this:


``` php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;

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

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function getJWTCustomClaims()
    {
        return [];
    }
    
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }
}
``` 


* You have to create the routes for the AuthJwtController in your routes/api.php file

``` php
use App\Http\Controllers\ApiController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthJwtController;

Route::controller(AuthJwtController::class)->group(function () {
    Route::post('login', 'login');
    Route::post('refresh', 'refresh');
    Route::post('blacklist', 'blacklist');
    Route::post('logout', 'logout');
    Route::get('get-token/{user_id}', 'getTokenByUser');
});
``` 

* You have to add yours private endpoints in the middleware auth:jwt, like this:

``` php

Route::controller(ApiController::class)->middleware('auth:api')->group(function () {
    Route::get('private-endpoint', 'privateEndopint');
});

```
* You can configure the time of expiration of the tokens by adding this to the .env file
```
JWT_TTL=60
```

> You can see more configurations in config/jwt.php file



* Finally, you have to create the AuthJwtController


``` php


use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;

class AuthJwtController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

    public function login(Request $request): JsonResponse
    {
        $validate = Validator::make($request->all(), [
            'email' => 'required|string|email',
            'password' => 'required|string',
        ]);

        if ($validate->fails()) {
            return response()->json([
                'status' => false,
                'error_message' => $validate->errors(),
            ], 400);
        }

        $credentials = $request->only('email', 'password');
        $token = Auth::guard('api')->attempt($credentials);

        if (!$token) {
            return response()->json([
                'status' => false,
                'message' => 'Unauthorized',
            ], 401);
        }

        return response()->json([
            'status' => true,
            'authorisation' => [
                'token' => $token,
                'type' => 'bearer',
            ],
        ]);
    }

    //if you want use this methods for yourself, add role in your user model and you validate in this methods

    public function refresh(): JsonResponse
    {
        return response()->json([
            'status' => true,
            'authorisation' => [
                'token' => Auth::refresh(true),
                'type' => 'bearer',
            ],
        ]);
    }

    public function blackList(): JsonResponse
    {
        //if you want add to blacklist forever, pass true as parameter
        Auth::invalidate();

        return response()->json([
            'status' => true,
            'message' => 'token added to blacklist successfully'
        ], 200);
    }

    public function logout(): JsonResponse
    {
        Auth::logout();

        return response()->json([
            'status' => true,
            'message' => 'logout successfully'
        ], 200);
    }

    public function getTokenByUser(Request $request): JsonResponse
    {
        $validate = Validator::make(['user_id' => $request->user_id], [
            'user_id' => 'required',
        ]);

        if ($validate->fails()) {
            return response()->json([
                'status' => false,
                'error_message' => $validate->errors(),
            ], 400);
        }

        if (!Auth::tokenById($request->user_id)) {
            return response()->json([
                'status' => false,
                'error_message' => "There aren't Token with this user id",
            ], 400);
        }

        return response()->json([
            'status' => true,
            'token' => Auth::tokenById($request->user_id)
        ]);
    }
}
```

After you have completed the steps, You can use your JWT authentication in your Laravel app.

let's start to test the JWT authentication.

## POSTMAN <a name="Postman"></a>

When we are testing our private endpoints that have a middleware with JWT we should hit the /login endpoint with the credentials copy the token and then embed it in the baren token of our private endpoint that has the JWT middleware.

Don't worry about that, follow the following steps to automate a pre-request that sets the JWT token in the bearer token to our private endpoint.

* First you must import the collection into your Postman app

[ Collection Link](https://github.com/germanlozickyj/jwt-auth-in-laravel/blob/main/tests/Postman/jwt-collection.json)

* Import the environment in your Postman app

[Environment Link](https://github.com/germanlozickyj/jwt-auth-in-laravel/blob/main/tests/Postman/jwt-enviroment.json)

* You have to select the environment imported
* You have to create an endpoint inside of the collection imported and put the authorization Bearer Token like this:

![Implement bearer token in Postman request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3qgcrkmif6ibkr66s7s.png)


Do you not know how to import environments and collections on Postman?

Don't worry, learn about that in the following links:

[How to import a collection](https://www.youtube.com/watch?v=-4CNWIPJDgo)

[How to import an environment](https://www.youtube.com/watch?v=_cw439gxLu0)