Web Analytics Made
Easy - StatCounter

Menu

About us Contact us

JWT Authentication in a Laravel Project

Tech 2019 / 08 / 22

JWT Authentication in a Laravel Project



One of the core features of a website is authorization and it can be implemented in many ways. The way I like to implement authentication is by JWT authentication.

JWT stands for JSON Web Token. JWT defines a way to transmit information between parties securely using JSON object. You can learn more about JWT from here.

We are going to integrate JWT authentication in our Laravel project and then we will check it with Postman.

If you haven’t created a Laravel project already, please create one. Lets name the project jwt-laravel. As of writing this article the latest Laravel version is 5.8.

Run the necessary migration command

php artisan migrate

and make sure the database is working perfectly.

Now lets start integrating JWT authentication in our project.

Step 1: Install the JWT package

We will be using the jwt-auth package by Sean Tymon.

composer require tymon/jwt-auth 1.0.*

This will install the JWT package with the necessary files.

Step 2: Publish config

We will publish the config for our JWT auth.

php artisan vendor:publish --provider=”Tymon\JWTAuth\Providers\LaravelServiceProvider”

You can see there is a new file jwt.php created in the config folder.

Step 3: Create JWT secret key

php artisan jwt:secret

A JWT secret key will be created in our .env file. Never share this key with anyone.

Step 4: Modify User Model

We will modify our User model to include some more methods. First we will implement the JWTSubject contract on our User model. Then we will add some methods named getJWTIdentifier(), getJWTCustomClaims() and setPasswordAttribute()

The model will look like this

<?php
 
namespace App;
 
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
 
class User extends Authenticatable implements JWTSubject
{
   use 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',
   ];
 
   /**
    * 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 [];
   }
 
   public function setPasswordAttribute($password)
   {
       if ( !empty($password) ) {
           $this->attributes['password'] = bcrypt($password);
       }
   }
}

Step 5: Configure Auth

We will need to configure the auth guard to make the system use our JWT authentication. By default Laravel uses web guard which uses the sessiondriver. We will change it to use the api guard and change the driver to jwt. So in config/auth.php we will make some changes so that the config inside will look similar to this

'defaults' => [
     'guard' => 'api',
     'passwords' => 'users',
],
   
'guards' => [
     'web' => [
         'driver' => 'session',
         'provider' => 'users',
     ],
 
     'api' => [
         'driver' => 'jwt',
         'provider' => 'users',
     ],
],

Step 6: Make AuthController

We will create an auth controller named AuthController. To make the controller we will run the following command

php artisan make:controller AuthController

Then we put the following code in the file.

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
 
class AuthController extends Controller
{
   /**
    * Get a JWT via given credentials.
    *
    * @return \Illuminate\Http\JsonResponse
    */
   public function login()
   {
       $credentials = request(['email', 'password']);
 
       if (! $token = auth()->attempt($credentials)) {
           return response()->json(['error' => 'Unauthorized'], 401);
       }
 
       return $this->respondWithToken($token);
   }
 
   /**
    * Get the authenticated User.
    *
    * @return \Illuminate\Http\JsonResponse
    */
   public function me()
   {
       return response()->json(auth()->user());
   }
 
   /**
    * Log the user out (Invalidate the token).
    *
    * @return \Illuminate\Http\JsonResponse
    */
   public function logout()
   {
       auth()->logout();
 
       return response()->json(['message' => 'Successfully logged out']);
   }
 
   /**
    * Refresh a token.
    *
    * @return \Illuminate\Http\JsonResponse
    */
   public function refresh()
   {
       return $this->respondWithToken(auth()->refresh());
   }
 
   /**
    * Get the token array structure.
    *
    * @param  string $token
    *
    * @return \Illuminate\Http\JsonResponse
    */
   protected function respondWithToken($token)
   {
       return response()->json([
           'access_token' => $token,
           'token_type' => 'bearer',
           'expires_in' => auth()->factory()->getTTL() * 60
       ]);
   }
}

Step 7: Add Routes

Next we add some routes in our routes/api.php file for the basic authentication. The file will look similar to this

<?php
 
use Illuminate\Http\Request;
 
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
 
Route::middleware('auth:api')->get('/user', function (Request $request) {
   return $request->user();
});
 
Route::group([
 
   'middleware' => 'api',
 
], function ($router) {
 
   Route::post('login', 'AuthController@login');
   Route::post('logout', 'AuthController@logout');
   Route::post('refresh', 'AuthController@refresh');
   Route::post('me', 'AuthController@me');
});

JWT authentication integration is complete. If we have a user and we input valid user data in route like localhost:80/api/login we should be able to get access token for that user.

So how do we check it for our current project? We don’t have any users. Lets just create a user controller with the following command

php artisan make:controller UserController

Next put the following code into the controller.

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
 
use App\User;
 
class UserController extends Controller
{
   /**
    * Display a listing of the resource.
    *
    * @return \Illuminate\Http\Response
    */
   public function index()
   {
       $users = User::all();
 
       return $users;
   }
 
   /**
    * Store a newly created resource in storage.
    *
    */
   public function store(Request $request)
   {
       $userData = $request->all();
       $user = User::create($userData);
 
       return $user;
   }
}

Lets modify our routes/api.php to include the following code

Route::post(‘users’, ‘UserController@store’);
Route::get(‘users’, ‘UserController@index’);

Our project is now ready to be tested for working authentication in Postman.

Let’s create a new user. We will use this user for authentication.

Open Postman. Enter the url localhost:80/api/users . Select the method as POST. In the body section add the necessary name, email, password and press the send button. You will get a response like this.

After the user creation is done. Lets enter a new url for login localhost:80/api/login . Set the method as GET. In the body section select form data and enter the value of email and password. Press send and you will get a response like this.

login with postman

 

It contains the information about the access token which will be used for all the necessary authentication tasks.

So if you want to access an api which requires an access token you have put the token as a Bearer Token in authorization. For example, if you want to know the details of the logged in user, you have to go the link localhost:80/api/me . You will get an empty response like below because you didn’t pass an access token.

To get more detailed error messages, such as: ‘Token is not valid’, ‘Token has expired’ etc we can add validation in the individual methods but the best practice would be to make a middleware that checks if a token is valid for specific requests.

So to do that lets create a new middleware named JWTMiddleware

php artisan make:middleware JWTMiddleware

Now add the following code into the newly created middleware.

<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Exception;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class JWTMiddleware extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        try {
            $user = JWTAuth::parseToken()->authenticate();
        } catch (Exception $e) {
            if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
                return response()->json(['status' => 'Token is Invalid']);
            }else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
                return response()->json(['status' => 'Token has Expired']);
            }else{
                return response()->json(['status' => 'Authorization Token not found']);
            }
        }
        return $next($request);
    }
}

Next add the next lines of code in the alias array in the config/app.php file.

'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,

This will register the facades necessary for the middleware. Next, we will add the following code in routeMiddleware array of our app/Http/Kernel.php file.

'jwt.verify' => \App\Http\Middleware\JWTMiddleware::class,

Here we are giving our middleware a name jwt.verify to be recognized by our system.

Our middleware is now ready. To check for a specific route or method we will add the middleware to the necessary routes. Lets modify our routes/api.php file to include the jwt.verify middleware.

Route::post('login', 'AuthController@login');Route::group([
    'middleware' => ['api','jwt.verify'],
], function ($router) {
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('me', 'AuthController@me');
});

Notice we have moved the login route outside of the group as that doesnt need the JWT validation. So now if you try the localhost:80/api/me route again without the access token, you will get a response similar to the one below.

You will have to put the bearer token in the authorization->Bearer Token field. Copy the access_token you got after login and paste it in the Token field. And then try the api again. This time you will get a response like this.

You can follow the same steps to get refresh tokens and also for logout.

So that is it for the basic JWT authentication integration in Laravel. In this article we have learned:

・Setting up JWT authentication

・Creating User

・Testing with Postman

・Adding middleware to verify token

If you want the source code and immediately want to run the project then you can go to the repository and follow the readme file:

 ーfaisalislamraju/jwt-laravel

You can follow the official documentation for the jwt-auth package to learn more.

・・・

Check out other articles from our engineering team:

https://medium.com/monstar-lab-bangladesh-engineering


Related Posts :

JWT auth in Go

JWT auth in Go Part 2 — Refresh Tokens

You have ideas, We have solutions.

CONTACT US!