DEV Community

Cover image for Setup Laravel 9 with JWT-Authentification
Abdelmajid E.
Abdelmajid E.

Posted on

Setup Laravel 9 with JWT-Authentification

Are you looking to start building a project with backend laravel and send API requests using Rest API here is a good tutorial.

In this tutorial, I will build a simple backend project with laravel and Tymon JWT-auth.

Setup a Laravel Project using Composer
There are a few options when it comes to installing Laravel. We will be using Composer to setup the Laravel framework.

For this you will need to install the following:

Composer
Node
And for development, you will be needing PHP 8.

After installing all of this, we can simply run the following command to scaffold a complete Laravel project:

composer create-project Laravel/laravel laravel-jwt-rest-api

Setup a .env file database connexion

First, create a database by the same name of the project "laravel-jwt-rest-api", add connexion to .env file:



DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_jwt_rest_api
DB_USERNAME=root
DB_PASSWORD=


Enter fullscreen mode Exit fullscreen mode

Now, Let's Install JWT Auth
composer require tymon/jwt-auth --ignore-platform-reqs
And you need to install laravel to generate jwt encryption keys. This command will create the encryption keys needed to generate secure access tokens:

php artisan jwt:secret

After successfully install laravel jwt, register providers. Open config/app.php . and put the bellow code :



'providers' => [
….
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
'aliases' => [
….
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
],


Enter fullscreen mode Exit fullscreen mode

JWT auth package comes up with middlewares that we can use. Register auth.jwt middleware in

app/Http/Kernel.php



protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'jwtAuth' => \App\Http\Middleware\JWTMiddleware::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];


Enter fullscreen mode Exit fullscreen mode

Now we need to modify User model. Open App/Models/User.php file and implement Tymon\JWTAuth\Contracts\JWTSubject interface. We also need to add two model methods getJWTIdentifier() and getJWTCustomClaims().



<?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 Tymon\JWTAuth\Contracts\JWTSubject;

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

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

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


Enter fullscreen mode Exit fullscreen mode

Next, the default authentication guard is web. We need to change it to api. Open config/auth.php file and change default guard to api.



<?php

return [


    'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],


    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
            'hash'=>false,
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
    ],


    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],


    'password_timeout' => 10800,

];




Enter fullscreen mode Exit fullscreen mode

Add Authentication routes

We need to register authentication routes into routes/api.php file. Open the file and add below routes into it.



// user
Route::post('/login',  [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
Route::get('/logout', [AuthController::class, 'logout'])->middleware("jwtAuth");
Route::post('/refresh', [AuthController::class, 'refresh'])->middleware("jwtAuth");
Route::get('/user-profile', [AuthController::class, 'getUser'])->middleware("jwtAuth");


Enter fullscreen mode Exit fullscreen mode

Create AuthController controller class

We have defined routes for authentication so far. We need to create a controller class to build application logic. The below Artisan command will generate a controller class at App/Http/Controllers/Api directory.



php artisan make:controller AuthController


Enter fullscreen mode Exit fullscreen mode

In the controller class, add the methods as per routes.



<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Validator;
use App\Models\User;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Symfony\Component\HttpFoundation\Response;
use Tymon\JWTAuth\Facades\JWTAuth;

class AuthController extends Controller
{
    public $token = true;

    public function register(Request $request)
    {

         $validator = Validator::make($request->all(), 
                      [ 
                      'name' => 'required',
                      'email' => 'required|email',
                      'password' => 'required',  
                      'c_password' => 'required|same:password', 
                     ]);  

         if ($validator->fails()) {  

               return response()->json(['error'=>$validator->errors()], 401); 

            }   


        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();

        if ($this->token) {
            return $this->login($request);
        }

        return response()->json([
            'success' => true,
            'data' => $user
        ], Response::HTTP_OK);
    }

    public function login(Request $request)
    {
        $input = $request->only('email', 'password');
        $jwt_token = null;

        if (!$jwt_token = JWTAuth::attempt($input)) {
            return response()->json([
                'success' => false,
                'message' => 'Invalid Email or Password',
            ], Response::HTTP_UNAUTHORIZED);
        }

        return response()->json([
            'success' => true,
            'token' => $jwt_token,
            'user'=> Auth::user(),
            ]);
    }

    public function logout(Request $request)
    {

        try {
            JWTAuth::invalidate(JWTAuth::parseToken($request->token));

            return response()->json([
                'success' => true,
                'message' => 'User logged out successfully'
            ]);
        } catch (JWTException $exception) {
            return response()->json([
                'success' => false,
                'message' => 'Sorry, the user cannot be logged out'
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }

    }

    public function getUser(Request $request)
    {
        try{
            $user = JWTAuth::authenticate($request->token);
            return response()->json(['user' => $user]);

        }catch(Exception $e){
            return response()->json(['success'=>false,'message'=>'something went wrong']);
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

We have created methods for authenticating APIs for Login, Register, getUser, Token Refresh, and Logout routes.

Create JWTMiddleware controller class

We need to protect our routes before the user gets into the controllers, for that we need to create a middleware JWTMiddleware using the command :



php artisan make:middleware JWTMiddleware


Enter fullscreen mode Exit fullscreen mode

The JWTMiddleware contains the verification of the Token sent with API.




<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;

class JWTMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
        $message = '';
        try {
            // check validation of the token
            JWTAuth::parseToken()->authenticate();
            return $next($request);
        } catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
            $message = 'Token expired';
        } catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
            $message = 'Invalid token';
        } catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
            $message = 'Provide token';
        }
        return response()->json(['success' => false, 'message' => $message]);
    }
}



Enter fullscreen mode Exit fullscreen mode

Do the migration

First, you need to create a database by the name laravel_jwt_rest_api,then run this command :



php artisan migrate



Enter fullscreen mode Exit fullscreen mode

Test application in Postman

We have completed the application coding. Start the Laravel server using below Artisan command.



php artisan serve


Enter fullscreen mode Exit fullscreen mode

For testing APIs, we will use Postman application. Postman is an API platform for building and using APIs. We will test all API. Lets start from register API.

Register API

All API routes are prefixed with api namespace. In the postman use http://127.0.0.1:8000/api/register API endpoint. Pass name, email, password and c_password parameters into request. You will get message and user details into response.

Image description

Login API

Use http://127.0.0.1:8000/api/login API endpoint with email password parameter with request. If the email and password matches with registered user, you will receive token json object into response

Image description

GetUser API

All JWTMiddleware middleware routes are protected with api guard. You need to pass access_token in Header as bearer token.

Image description

Image description

Logout API

To logout the user, you need to invalidate the current token. You can simply call auth()->logout() method to invalidate current access token. Once user, logged out, it can't access protected routes.

Image description

Conclusion

Eventually, our tutorial is over. We have learned how to implement JWT authentication in Laravel application. In the next tutorial, we will use JWT token for REST API.

I hope, this tutorial will help on your development. If you liked this tutorial, please consider to share with your friends.

Top comments (2)

Collapse
 
pedro_bortolini profile image
Pedro

Amazing content!

The middleware is ignoring the return value of authenticate(). This can result in a valid token without an identified user. If the 'sub' value in the token is incorrect or the user cannot be found, the token will still be valid, but methods like JWTAuth::user() will not work correctly.

To fix this, consider the return value of authenticate():

$authenticatedUser = JWTAuth::parseToken()->authenticate();
if ($authenticatedUser) {
    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

This ensures that the user is correctly identified and avoids issues when using user-related methods.

Collapse
 
justwonder profile image
William Onyejiaka

The custom jwt middleware was helpful.Thank you.