Chapter 17 - Authentication with JWT

Hello Artisan's, welcome to the 11th chapter of being an Artisanary. In this chapter we'll see authentication with JWT(Json-Web-Token). In this chapter we'll protect the previous chapters CRUD for AUTH user only. So if you already complete the previous chapters/sections, then you're good to go, if not my recommendation would be please complete the previous chapters. Because we'll use the same old repository that we use in chapter 4. 

Note: Tested on Laravel 10.0

Table of Contents

  1. install and setup tymon/jwt-auth
  2. Create and Setup Middleware
  3. Create and Setup Controller
  4. Create Route
  5. Output

Install and setup tymon/jwt-auth

tymon/jwt-auth is quite popular package for handling jwt. It's also easy to use and provide lots of handful methods which is reduce our time conveniently. So, fire the below commands in the terminal.

composer require tymon/jwt-auth 
//after successfully install
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret

After running the above commands replace your User.php code with the below one.

app/Models/User.php
<?php

namespace App\Models;

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

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

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

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

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

    public function role(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(Role::class);
    }

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims(): array
    {
        return [];
    }
}

And then we need to update guard array in config/auth.php as below

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

And it'll configure our jwt successfully.

Create and Setup Middleware

First of all, we'll create a middleware for checking if a request comes with token or not. So, fire the below commands to create middleware.

 php artisan make:middleware JWTMiddleware

It'll create a file called app/Http/Middleware/JWTMiddleware.php, open the file and replace it with the below codes

JWTMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Facades\JWTAuth;

class JWTMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        try {
            $user = JWTAuth::parseToken()->authenticate();
        } catch (\Exception $e) {
            if ($e instanceof TokenInvalidException)
                return response()->json([
                    'error' => __('Token is invalid')
                ]);
            elseif ($e instanceof TokenExpiredException)
                return response()->json([
                    'error' => __('Token is Expired')
                ]);
            else
                return response()->json([
                    'error' => __('Authorization Token Not Found')
                ]);
        }
        if ($user):
            return $next($request);
        else:
            return response()->json([
                'error' => __('Invalid Token')
            ]);
        endif;
    }
}


After working middleware we need to register in $middlewareAliases in app/Http/Kernel.php as below

Kernel.php
protected $middlewareAliases = [
			.......
			'jwt_auth' => JWTMiddleware::class,
    ];

Create and Setup Controller

At first we need to create a middleware where we'll write our logic to get the token. So, that user can use the token and can authenticated in the system. So, fire the below command in your terminal

 php artisan make:controller Api/LoginController

Which'll create a controller called LoginController.php under app/Http/Controllers/Api. Open the file and replace it with below codes

LoginController.php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Facades\JWTAuth;

class LoginController extends Controller
{
    public function login(Request $request): \Illuminate\Http\JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'email' => 'required',
            'password' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'errors' => $validator->errors(),
            ], 422);
        }
        try {
            $user = User::where('email', $request->email)->first();

            if (!$user)
            {
                return response()->json([
                    'error' => 'User not found'
                ], 422);
            }

            $credentials = $request->only('email', 'password');

            try {
                if (!$token = JWTAuth::attempt($credentials)) {
                    return response()->json([
                        'error' => 'Invalid Credentials',
                    ], 422);
                }
            } catch (JWTException $e) {
                return response()->json([
                    'error' => 'Could not create token'
                ], 422);

            } catch (\Exception $e) {
                return response()->json([
                    'error' => $e->getMessage()
                ], 422);
            }

            Auth::attempt($credentials);

            return response()->json([
                'token' => $token,
                'user' => $user
            ], 200);
        } catch (\Exception $e) {
            return response()->json([
                'error' => $e->getMessage()
            ], 422);
        }
    }
}

Setup Route

Now put the below route to routes/api.php

api.php
<?php

use App\Http\Controllers\Api\BlogController;
use Illuminate\Support\Facades\Route;

Route::post('login', [\App\Http\Controllers\Api\LoginController::class, 'login'])->name('login');

Route::resource('blogs', BlogController::class)->except(['edit','create','update'])->middleware('jwt_auth');
Route::post('blogs/{id}', [BlogController::class, 'update'])->name('blogs.update')->middleware('jwt_auth');

Output

Now we need to create a request(not discussed here, already explained in previous chapter) called login after giving the url hit Send. Then you'll get the below result.

Now we need to copy the token and paste it into Authorization because for checking, if the user is authenticated or not the token must be in header or parameter. So, if we paste it in Authorization part, it'll always appears in headers. See the image below to for more info

 

Now after setup if we hit the Send button for index request then we can see the below result.

And if we don't provide the token in header then we can have the below error.

So, it's time to say goodbye for today. We saw the authentication with JWT. And yes keep up to date with the Github repository. That's it for today. See you in my next chapter. Happy coding ๐Ÿ™‚.