Implementation of laravel JWT multi table (multi client) authentication isolation

Time:2021-11-23

Tips: the author of tymon / JWT auth has fixed this problem by adding PRV field #1167, but if you use dingo API + JWT, the problem still exists#

JWT multi table verification isolation

Why isolation

When the same laravel project has multiple terminals (mobile terminal, management terminal…) that need to use JWT for user authentication, if there are multiple user tables (generally), token isolation is required, otherwise the mobile terminal’s token can also request the management terminal, resulting in the user exceeding his authority.

The reason for this problem is that the JWT token of laravel only stores the value of the primary key of the data table by default, and does not distinguish which table it belongs to. Therefore, as long as the ID carried in the token exists in your user table, it will lead to unauthorized authentication.

Let’s take a look at the original appearance of laravel’s JWT token:


{
 "iss": "http://your-request-url",
 "iat": 1558668215,
 "exp": 1645068215,
 "nbf": 1558668215,
 "jti": "XakIDuG7K0jeWGDi",
 "sub": 1
}

The sub field carries the data, and the other fields are JWT’s verification fields.

We only see that the value of sub is 1, and we do not specify which table or verifier it belongs to. When this token passes through your authentication middleware, you can use different guards to get the user with the corresponding table ID of 1 (please check the laravel documentation for guard).

terms of settlement

To solve the problem of users exceeding their authority, we just need to bring our custom fields on the token to distinguish which table or verifier generated them, and then write our own middleware to verify whether our custom fields meet our expectations.

Add custom information to token

We know that to use JWT authentication, the user model must implement the interface of jwtsubject (the code is taken from the JWT document):


<?php

namespace App;

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

class User extends Authenticatable implements JWTSubject
{
 use Notifiable;

 // Rest omitted for brevity

 /**
  * 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 [];
 }
}

We can see the functions of these two methods:

  • Getjwtidentifier: to obtain the identifier that will be stored in the JWT declaration, in fact, we need to return the name of the primary key field of the user identification table. Here, the returned primary key ‘ID’,
  • Getjwtcustomclaims: returns an array containing custom key value pairs to be added to the JWT declaration. Here, an empty array is returned without adding any custom information.

Next, we can add our custom information to the user model that implements the getjwtcustomclaims method.

Administrator model:

/**
 *Additional customization added in JWT load
 *
 * @return array
 */
public function getJWTCustomClaims()
{
 return ['role' => 'admin'];
}

Mobile end user model:

/**
 *Additional customization added in JWT load
 *
 * @return array
 */
public function getJWTCustomClaims()
{
 return ['role' => 'user'];
}

A role name is added here as the user ID.

In this way, the token generated by the administrator will look like this:


{
 "iss": "http://your-request-url",
 "iat": 1558668215,
 "exp": 1645068215,
 "nbf": 1558668215,
 "jti": "XakIDuG7K0jeWGDi",
 "sub": 1,
 "role": "admin"
}

The token generated by the mobile user will look like this:


{
 "iss": "http://your-request-url",
 "iat": 1558668215,
 "exp": 1645068215,
 "nbf": 1558668215,
 "jti": "XakIDuG7K0jeWGDi",
 "sub": 1,
 "role": "user"
}

We can see that there is an additional role field added by ourselves, which corresponds to our user model.

Next, we write a middleware ourselves. After parsing the token, we judge whether it is the role we want. If it corresponds, we pass. If not, we report 401.

Write JWT role verification Middleware

A middleware that can be used globally is provided here (recommended before user authentication Middleware):

<?php
/**
 * Created by PhpStorm.
 * User: wlalala
 * Date: 2019-04-17
 * Time: 13:55
 */

namespace App\Http\Middleware;

use Closure;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class JWTRoleAuth extends BaseMiddleware
{
 /**
  * Handle an incoming request.
  *
  * @param $request
  * @param Closure $next
  * @param null $role
  * @return mixed
  */
 public function handle($request, Closure $next, $role = null)
 {
  try {
   //Resolve token role
   $token_role = $this->auth->parseToken()->getClaim('role');
  } catch (JWTException $e) {
   /**
    *Token parsing failed, indicating that there are no available tokens in the request.
    *In order to be used globally (requests that do not require a token can also be passed), let the request continue here.
    *Because the responsibility of this middleware is only to verify the role in the token.
    */
   return $next($request);
  }

  //Judge the token role.
  if ($token_role != $role) {
   throw new UnauthorizedHttpException('jwt-auth', 'User role error');
  }

  return $next($request);
 }
}

Register JWT role verification Middleware

Register Middleware in APP / HTTP / kernel.php:

/**
  * The application's route middleware.
  *
  * These middleware may be assigned to groups or used individually.
  *
  * @var array
  */
 protected $routeMiddleware = [
  //... omit

  //Multi table JWT verification
  'jwt.role' => \App\Http\Middleware\JWTRoleAuth::class,
 ];

Verifying middleware using JWT role

Next, add our middleware to the routing group requiring user authentication:

Route::group([
 'middleware' => ['jwt.role:admin', 'jwt.auth'],
], function ($router) {
 //The administrator verifies the route
 // ...
});

Route::group([
 'middleware' => ['jwt.role:user', 'jwt.auth'],
], function ($router) {
 //Mobile user authentication routing
 // ...
});

This completes JWT multi table user authentication isolation.

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support developpaer.

Recommended Today

Apache sqoop

Source: dark horse big data 1.png From the standpoint of Apache, data flow can be divided into data import and export: Import: data import. RDBMS—–>Hadoop Export: data export. Hadoop—->RDBMS 1.2 sqoop installation The prerequisite for installing sqoop is that you already have a Java and Hadoop environment. Latest stable version: 1.4.6 Download the sqoop installation […]