Laravel 6 Rest API using JWT Authentication

Laravel 6 Rest API using JWT Authentication

JWT authentication provides a secure way to transmit data between the client and server using Laravel APIs. In this post, we will look at how we can create a JWT authentication for a Laravel based API.

API’s provide a fluent interface to communicate with the different type of services. If you are trying to build an application which you want to provide to end-users on different platforms such as web, iOS or Android, then using an API approach will be great as you can code different frontend application without changing your backend code.

Before jumping into JWT, let’s find out what is JWT?

What is JSON Web Token?

JWT stands for JSON Web Token and is an open standard to securely transmitting information between different parties (clients and servers) as a JSON (JavaScript Object Notation) object.

In JWT, the information can be trusted or verified based on the digital signature it carries along with it. Usually, we use the signed tokens to verify the information.

When to Use JSON Web Tokens

There are various scenarios where you can use the JWT to transfer data or information more securely. One of the most popular usages of JWT is in API authorization. Once a user is logged in successfully, all subsequent request will include the JWT which can be used to access the routes, services and only allow the resources which you want to be accessible.

Another usage of JWT authentication is for exchanging/sharing information is a good secure way. Because JWTs can be signed off using the public/private key pairs – you can be sure that the sender who is making the request is trusted.

Why You Should Use JWT?

Laravel uses a session-based authentication system that comes out of the box when you create a new application. If you have a look in the config/auth.php file, you can find out that by default Laravel authentication configurations.

/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],

Laravel’s authentication mechanism is based on the guards and providers. Gurads define how users are authenticated for each request, while providers defines the guard’s configuration. By default Laravel utilize the sessions and cookies.

Sessions or cookies are great for authentication when your client and server application are in the same domain. But in the case of an API, you are likely to have a client on a different domain and your backend application on a separate domain.

In this case, you will end up with the Cross Origin Resource Sharing (CORS) errors.

When building applications in a commercial environment, it’s a common practice to use the JSON Web Tokens (JWT) instead, which means if you are using Laravel 6 then you have to update the guard’s configuration and use JWT instead of sessions.

From the next section, we will start by installing Laravel and create an API that will use the JWT authentication.

Application Planning

Before we start coding we need to define a roadmap that we will be building in this post. We will be building an API for Task Management with the JWT Authentication.

  • A user can create a new account.
  • A user can log into their account.
  • User can logout to dicard the authentication token.
  • Get all tasks related to a User.
  • Find a specific task by Id.
  • Add a new task to User’s task list.
  • Edit an existing task details.
  • Delete an existing task from the user list.

Here are the two Eloquent models User and Task.

A User will requires:

  • name
  • email
  • password

A Task will required:

  • title
  • description
  • user id

Creating a New Laravel 6 Application

Let’s start by creating a new Laravel 6 application. Run the below command on the command line terminal to generate a new Laravel application.

composer create-project laravel/laravel JWTApp

It will create a new Laravel application in the folder named JWTApp. If you face any problem in installing Laravel, please check out our post on Install Laravel on Windows or Linux.

Installing JWT Authentication Package

Once you have the Laravel application created, we will install the tymondesigns/jwt-auth package for working with the JWT authentication in Laravel.

Run the below command in the terminal to install this package.

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

When package installation finishes run the below command to publish the package configurations.

php artisan vendor:publish

The above command will provide you a list of all discoverable packages, choose the Provider: Tymon\JWTAuth\Providers\LaravelServiceProvider from the list and hit enter.

You will have something like the below response in the console.

λ php artisan vendor:publish

 Which provider or tag's files would you like to publish?:
  [0 ] Publish files from all providers and tags listed below
  [1 ] Provider: Facade\Ignition\IgnitionServiceProvider
  [2 ] Provider: Fideloper\Proxy\TrustedProxyServiceProvider
  [3 ] Provider: Illuminate\Foundation\Providers\FoundationServiceProvider
  [4 ] Provider: Illuminate\Mail\MailServiceProvider
  [5 ] Provider: Illuminate\Notifications\NotificationServiceProvider
  [6 ] Provider: Illuminate\Pagination\PaginationServiceProvider
  [7 ] Provider: Laravel\Tinker\TinkerServiceProvider
  [8 ] Provider: Tymon\JWTAuth\Providers\LaravelServiceProvider
  [9 ] Tag: config
  [10] Tag: flare-config
  [11] Tag: ignition-config
  [12] Tag: laravel-errors
  [13] Tag: laravel-mail
  [14] Tag: laravel-notifications
  [15] Tag: laravel-pagination
 > 8
8

Copied File [\vendor\tymon\jwt-auth\config\config.php] To [\config\jwt.php]
Publishing complete.
Publishing complete.

Above command has generated a jwt.php configuration file in the config folder. Feel free to open this file and check which settings are available through this package.

Generating JWT Authentication Keys

JWT authentication token will be signed with an encryption key, run the following command to generate the secret key used to sign the tokens.

php artisan jwt:secret

You will have something like the below output.

λ php artisan jwt:secret
jwt-auth secret [pUSAT5tCxJLHT28RNGMLpbgis3J6MD2NUEDJQtgeGYJgwBVLk9kTwEA4WSNmn3og] set successfully.

Registering JWT Middleware

JWT package comes with a pre built middleware which we can use for our API routes. Open the app/Http/Kernel.php file and register this middleware with the name auth.jwt.

 /**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::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,
    'auth.jwt'  =>  \Tymon\JWTAuth\Http\Middleware\Authenticate::class, // JWT middleware
];

This middleware will check if the user is authenticated by checking the token sent with the request if user is not authenticated it will throw an UnauthorizedHttpException exception.

Setting Up API Routes

In this section, we will setup our routes required for our this application. Open the routes/api.php file and copy the below routes to this file.

Route::post('login', '[email protected]');
Route::post('register', '[email protected]');

Route::group(['middleware' => 'auth.jwt'], function () {
    Route::get('logout', '[email protected]');

    Route::get('tasks', '[email protected]');
    Route::get('tasks/{id}', '[email protected]');
    Route::post('tasks', '[email protected]');
    Route::put('tasks/{id}', '[email protected]');
    Route::delete('tasks/{id}', '[email protected]');
});

Updating User Model

JWT package we are using requires implementing the Tymon\JWTAuth\Contracts\JWTSubject interface on our User model. This interface requirews to implement two methods getJWTIdentifier and getJWTCustomClaims in our User model.

Open the app/User.php file and update with the below one.

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
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 [];
    }
}

Creating Registration Form Request

From now on, we will start actually implementing the logic required for our API. Firstly, we will create a form request for registration purpose. User registration will require name, email, and password. So let’s create the form request class to handle this validation.

We will create a new form request class RegistrationFormRequest by running the command below.

php artisan make:request RegistrationFormRequest

It will create RegistrationFormRequest.php file in the app/Http/Requests folder.

Open this class and replace the code with below one:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegistrationFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string',
            'email' => 'required|email|unique:users',
            'password' => 'required|string|min:6|max:10'
        ];
    }
}

Creating API Controller for Login and Registration

Now, we will create a new controller class and name it APIController. Run the below command to generate this controller.

php artisan make:controller APIController

This will generate a new controller in the app/Http/Controllers folder. Open this controller and update with the below controller class.

namespace App\Http\Controllers;

use JWTAuth;
use App\User;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\Http\Requests\RegistrationFormRequest;
class APIController extends Controller
{
    /**
     * @var bool
     */
    public $loginAfterSignUp = true;

    /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
public function login(Request $request)
    {
        $input = $request->only('email', 'password');
        $token = null;

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

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

    /**
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     * @throws \Illuminate\Validation\ValidationException
     */
    public function logout(Request $request)
    {
        $this->validate($request, [
            'token' => 'required'
        ]);

        try {
            JWTAuth::invalidate($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'
            ], 500);
        }
    }

    /**
     * @param RegistrationFormRequest $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function register(RegistrationFormRequest $request)
    {
        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);
        $user->save();

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

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

In the above controller class, we firstly added all the required classes. Then we have defined a public property $loginAfterSignUp which we will use in our register() method.

First, function we added is the login(). In this function, we firstly get the subset data from the form request only containing email and password. By using the JWTAuth::attempt($input) method, we determine if the authentication is successful and save the response in the $token variable. If the response is false, then we are sending the error message back in JSON format. If the authentication return true then we send the success response along with the $token.

Next, we added the logout() method which invalidate the token. Firstly, we get the token from the form request and validate it, then call the JWTAuth::invalidate() method by passing the token from a form request. If the response is true we return the success message. If any exception occurs then we are sending an error message back.

In the final, register() method we get the data from the form request and create a new instance of the User model and save it. Then we check if the our public property $loginAfterSignup is set we call the login() method to authenticate the user and send the success response back.

Task Model and Migration

So far, we have implemented the authentication part, now we will create a new model Task and perform a CRUD operation on it.

For tasks, we will need to create a model, migration, and controller. Run the below command in command line terminal to generate model, migration, and controller altogether.

php artisan make:model Task -mc

Above command will create a new migration file in the database/migrations folder. Open that and update it with the below one.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTasksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedInteger('user_id');
            $table->string('title');
            $table->text('description')->nullable();
            $table->foreign('user_id')
                    ->references('id')
                    ->on('users')
                    ->onDelete('cascade');

            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}

Now, setup the database configuration in .env file and run the below command to migrate all tables.

php artisan migrate

Now, open the Task model from the app/ folder and update the model with below one.

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    /**
     * @var string
     */
    protected $table = 'tasks';

    /**
     * @var array
     */
    protected $guarded = [];
}

As we know already a task belongs to a user and a user can have many tasks. Open the User model and add the below method in it.

/**
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function tasks()
{
    return $this->hasMany(Task::class);
}

In the above method, we have added a One To Many relationship.

Task Controller

In this section, we will implement the CRUD functionality for our Task model. Open the TaskController from app/Http/Controllers folder and update with the below one.

namespace App\Http\Controllers;

use JWTAuth;
use App\Task;
use Illuminate\Http\Request;

class TaskController extends Controller
{
    /**
     * @var
     */
    protected $user;

    /**
     * TaskController constructor.
     */
    public function __construct()
    {
        $this->user = JWTAuth::parseToken()->authenticate();
    }
}

In the above code, we have defined a protected property and with the help of the __construct() method set the currently authenticated user to $user property.

The parseToken() method will get the token from the request object and authenticate() method will authenticate the user by token.

Next, we will add the index() method which will return the all tasks for a currently authenticated user.

/**
 * @return mixed
 */
public function index()
{
    $tasks = $this->user->tasks()->get(['title', 'description'])->toArray();

    return $tasks;
}

In the above method, we are getting all tasks for a user and converting them to an array.

Next, add a new method named show() which will grab a task by its id.

 /**
 * @param $id
 * @return \Illuminate\Http\JsonResponse
 */
public function show($id)
{
    $task = $this->user->tasks()->find($id);

    if (!$task) {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task with id ' . $id . ' cannot be found.'
        ], 400);
    }

    return $task;
}

The above example is fairly simple to understand. We simply find the task with the id. If the task is not present, a failure 400 response is returned. Otherwise, the task is returned.

Now, add the store() method which will be responsible for saving a task for user.

/**
 * @param Request $request
 * @return \Illuminate\Http\JsonResponse
 * @throws \Illuminate\Validation\ValidationException
 */
public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required',
        'description' => 'required',
    ]);

    $task = new Task();
    $task->title = $request->title;
    $task->description = $request->description;

    if ($this->user->tasks()->save($task))
        return response()->json([
            'success' => true,
            'task' => $task
        ]);
    else
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task could not be added.'
        ], 500);
}

In the store method, the request is validated that it contains title and description. Then, a new Task model is created with the data present in the request. If the task is saved successfully in the database, a success response is returned. Otherwise, a failure 500 custom response is returned.

Let’s implement the update method.

/**
 * @param Request $request
 * @param $id
 * @return \Illuminate\Http\JsonResponse
 */
public function update(Request $request, $id)
{
    $task = $this->user->tasks()->find($id);

    if (!$task) {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task with id ' . $id . ' cannot be found.'
        ], 400);
    }

    $updated = $task->fill($request->all())->save();

    if ($updated) {
        return response()->json([
            'success' => true
        ]);
    } else {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task could not be updated.'
        ], 500);
    }
}

In the update method, we find the task with the id. If the task does not exist, a 400 response is returned. Then we update the task with the data present in the request by using the fill() method. The updated task model is then saved in the database. If the record updates successfully, a 200 success response is returned. Otherwise, a 500 internal server error response is returned.

Finally, we will add the destroy() method.

/**
 * @param $id
 * @return \Illuminate\Http\JsonResponse
 */
public function destroy($id)
{
    $task = $this->user->tasks()->find($id);

    if (!$task) {
        return response()->json([
            'success' => false,
            'message' => 'Sorry, task with id ' . $id . ' cannot be found.'
        ], 400);
    }

    if ($task->delete()) {
        return response()->json([
            'success' => true
        ]);
    } else {
        return response()->json([
            'success' => false,
            'message' => 'Task could not be deleted.'
        ], 500);
    }
}

In the above method, we find the task by id. If a task is not present, a 400 response is returned. Then we delete the task and return an appropriate response.

That’s all for the TaskController, we have implemented the tasks CRUD successfully. Now it’s time for testing.

Testing API

To test our API, we will run the built-in PHP server. Open the command line terminal and run the below command.

php artisan serve

It will start a development server at localhost:8000.

To test this REST api I will using the Postman which is a collaborative environment for building APIs. You can read more about this application on their website.

I will be testing the registration functionality firstly and will try to register a user by filling the name, email, and password in the request body.

Register a User - Request
Register a User – Request

Send a request and you will get a token back like below.

Register a User - Response
Register a User – Response

Now our user is registered using the REST API. Now we will test the login functionality by sending a request to /api/login route.

User Login - Request
User Login – Request

It will return a 200 response with the token like below.

User Login - Response
User Login – Response

Logout functionality will work as expected you can give a try yourself.

Next, we will create a new task for the currently authenticated user.

Create Task using API
Create Task using API

Now we will get all tasks by visiting the /api/tasks route and pass the JWT token.

Getting All Tasks
Getting All Tasks

As you can see it’s working as expected. I will leave the other methods for you to test. If you face any problem let us know in the comments box below.

Final Words

In this post, we have successfully implemented the REST API with JWT authentication.

You can find the code base of this tutorial on Laravel 6 JWT repository.

If you have any question let us know in the comments box below.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

When sharing a code snippet please wrap you code with pre tag and add a class code-block to it like below.
<pre class="code-block">you code here</pre>

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.