Skip to content

Using JSON Web Tokens with Laravel to create APIs

Using JWT to authenticate users through APIs is especially useful since it does not require implementing sessions on the server and the JWT itself contains the information corresponding to the user making the request, which serves to validate their authenticity.

Learn more about JWT

First of all, we need to install Laravel and create a project. Instructions to install Laravel 5.8. After having Laravel installed, we proceed to create a project.

bash
$ laravel new webpage

To implement JWT in Laravel we will use the module called tymondesigns/jwt-auth with the following code:

bash
$ composer require tymon/jwt-auth:dev-develop --prefer-source

Then to generate the configuration file we run the following code:

bash
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

We proceed to generate a key for our project:

bash
$ php artisan jwt:secret

For Laravel to automatically connect to our database and use it as a user validation method, we must create a users table with the following columns:

  • id PRIMARY KEY AUTOINCREMENT
  • name VARCHAR
  • surname VARCHAR
  • email VARCHAR
  • password VARCHAR
  • create_at TIMESTAMP
  • updated_at TIMESTAMP

The User model will use the users table by default. If you want to specify another name for the table, you can define the name to use in app\User.php with the following code:

php
...
protected $table = ''; // we put the table name
...

The following steps are at the code level. We need to implement Tymon\JWTAuth\Contracts\JWTSubject in the User model. In this model we must implement two methods: getJWTIdentifier() and getJWTCustomClaims().

The User model in app\User.php should look similar to this:

php
<?php
namespace App;

use Illuminate\Notifications\Notifiable;  
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject {
    use Notifiable;
    
    protected $fillable = [
        'name', 'surname', 'email', 'password',
    ];
  
    protected $hidden = [
        'password', 'remember_token',  
    ];
    
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
  
    public function getJWTIdentifier() {
        return $this->getKey();
    }
    
    public function getJWTCustomClaims() {
        return [];  
    }
}

Now for Laravel to use JWT as an authentication method, we open the config/auth.php file and modify the following data:

php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',  
],
...
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],  
],

Now we proceed to add the authentication controller. We will call it AuthController. We can create it manually or with the following command:

bash
$ php artisan make:controller AuthController

In this controller we put the following code:

php
<?php
namespace App\Http\Controllers;

use App\Http\Requests\RegisterAuthRequest;  
use App\User;
use Illuminate\Http\Request;  
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthController extends Controller {
    public $loginAfterSignUp = true;
    
    public function register(Request $request) {
        $user = new User();
        $user->name = $request->name;
        $user->surname = $request->surname;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();
    
        if ($this->loginAfterSignUp) {
            return $this->login($request);
        }
    
        return response()->json([
            'status' => 'ok',
            'data' => $user
        ], 200);
    }
    
    public function login(Request $request) {
        $input = $request->only('email', 'password');
        $jwt_token = null;
        if (!$jwt_token = JWTAuth::attempt($input)) {
            return response()->json([
                'status' => 'invalid_credentials',
                'message' => 'Incorrect email or password.',
            ], 401);
        }
    
        return response()->json([
            'status' => 'ok',
            'token' => $jwt_token,
        ]);
    }
    
    public function logout(Request $request) {
        $this->validate($request, [
            'token' => 'required'
        ]);
    
        try {
            JWTAuth::invalidate($request->token);
            return response()->json([
                'status' => 'ok',
                'message' => 'Logout successful.'  
            ]);
        } catch (JWTException $exception) {
            return response()->json([
                'status' => 'unknown_error',
                'message' => 'The user could not be logged out.'
            ], 500);
        }
    }
    
    public function getAuthUser(Request $request) {
        $this->validate($request, [
            'token' => 'required'  
        ]);
    
        $user = JWTAuth::authenticate($request->token);
        return response()->json(['user' => $user]);
    }
}

Now we create the routes that will access these methods in routes\api.php:

php
<?php
use Illuminate\Http\Request;

// these routes can be accessed without providing a valid token.
Route::post('/login', 'AuthController@login');
Route::post('/register', 'AuthController@register');
// these routes require a valid token to be accessed.  
Route::group(['middleware' => 'auth.jwt'], function () {
     Route::post('/logout', 'AuthController@logout');
});

Additionally, we can add the following code at the beginning of public\index.php to avoid CORS errors during our tests:

php
// allows requests from any origin
header('Access-Control-Allow-Origin: *');
// allows requests with GET, PUT, POST, DELETE and OPTIONS methods  
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
// allows the Content-Type and Authorization headers
header('Access-Control-Allow-Headers: Content-Type, Authorization');

This concludes the configuration part of Laravel to use JSON Web Tokens. Now we proceed to make some test requests using Insomnia to verify that our routes are working.

We make a request to the /api/register route and confirm that we receive a status: ok and a token.

JWT registration test

This means that this user was registered in the database and a token was automatically generated that we must send with each request to protected routes.

We can also test /api/login.

JWT login test

The token can be sent in each request in different ways:

  • We can do it by url in the case of using GET for example .../api/products?token=eyJ0eXAiOiJ....
  • As a property of a JSON sent in a POST.
  • Or as part of the Authorization: Bearer eyJ0eXAiOiJ... header.

In the following example we send it as a property in a JSON to log out the user using api/logout.

JWT logout test

In the same way we could continue creating more protected routes which would require a valid token to be accessed. As always, any questions can be left in the comments. Regards.