Jwt-auth: Conflict with config field (user => 'App\User') when trying to using jwt-auth for multiple models

Created on 13 Aug 2016  路  5Comments  路  Source: tymondesigns/jwt-auth

So I have a scenario where I'm building an app which is one part Admin and one part API. So i've decided to use the default 'auth' middleware for the admin routes while i'm using the jwt-auth for my API. So on a side note I was just wondering what if I want to use the jwt-auth to authenticate both portions of my routes by creating custom providers, the JWTAuth::Authenticate() uses the settings in the provider for the selected route but the subsequent JWTAuth::parseToken()->toUser() uses the model defined in the user config field in config/jwt-auth.php file.

Most helpful comment

@chuajose @llioor

This seems like more of a StackOverflow question since this issue isn't related to JWT Auth, but a lack of understanding on how Laravel Guards work. In your controller you're using Auth::guard()->user();, but that just uses the default guard equivalent to Auth::guard(null)->user(); which uses the defaults['guard'] in auth.php. You'll want to do something like Auth::guard('customer')->user()' and Auth::guard('user')->user();.

In auth.php you'll have something like:

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

'guards' => [
    'user' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
    'customer' => [
        'driver' => 'jwt',
        'provider' => 'customers',
    ],
    // ...
],

'providers' => [

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

    'customers' => [
        'driver' => 'eloquent',
        'model' => App\Customer::class,
    ],
    // ...
]
````

Which means that for a particular `guard` you will use this `provider`, and within the model associated to the provider you'll have your distinguishing `getJWTCustomClaims` that has a key like `type` or `role` set to be `user` or `customer` so the tokens can be differentiated, and you can drop some logic in middleware to prevent routing to endpoints that don't accept that `type` or `role`.

You can do this a number of different ways, but since you only have the two types I'd use the `LoginController` as a base with a protected method `guard` and member variable `$guard`, create `UserLoginController` and `CustomerLoginController` and inherit the `LoginController`, and change only the `$guard` member variable to be either `user` or `customer` depending on the controller.  Then invoke `$this->guard()` to apply the specific guard during authentication and never hardcode it like `Auth::guard('customer')` directly only use `$this->guard()` so your `LoginController` has only generic authentication logic.

**LoginController Example**

```js
/**
 * Sets the guard to be used during authentication.
 *
 * @var string|null
 */
protected $guard = null;

// All your usually authentication methods using `$this->guard()`

/**
 * Gets the guard to be used during authentication.
 *
 * @return \Illuminate\Contracts\Auth\StatefulGuard
 */
protected function guard()
{
    return Auth::guard($this->guard);
}

CustomerLoginController

/**
 * Sets the guard to be used during authentication.
 *
 * @var string|null
 */
protected $guard = 'customer';

// Any other overrides required specifically for customer authentication

UserLoginController

/**
 * Sets the guard to be used during authentication.
 *
 * @var string|null
 */
protected $guard = 'user';

// Any other overrides required specifically for user authentication

Outside of authentication, I don't like creating multiple classes specific to a type of user and inheriting from a base class since on some projects you might have 6 types of users so I use middleware and an Actor class to access the token claims elsewhere in my application. So if you had an endpoint that was used by one or both types in the middleware that contains generic logic I instantiate a new Actor($token->type) and drop it into application container for reuse in my controllers. So now in the controllers I can using Actor I can get JWT claim information or a model of the authenticated user and it doesn't matter if they are a user or customer, and only when I'm forced to differentiate controllers based on a user type do I create user-specific classes and inherit from a base class.

All 5 comments

@laxx you might find it easier to use the development branch and instead use JWTGuard not JWTAuth. JWTGuard integrates directly into Laravel 5.2's new guard API and removes a lot of the overhead from multi-auth, and the requirements for middleware. Even more so with Laravel 5.3 updates to the middleware and integration into core.

5.2 setup can be found at #513
5.3 setup can be found at #860

Hello, I'm using JWTGuard with two models App\User and App\Customer and i change the guard to use from Route

Route::group([
    'prefix' => 'admin',
], function () {
    Route::post('login', 'Auth\LoginController@login');
    // Authentication Routes...
    Route::group(['middleware' => 'auth:api'], function(){
        Route::resource('home', 'HomeController');
         Route::get('logout', 'Auth\LoginController@logout');
    });
});
Route::group([
    'prefix' => 'customer',
], function () {
    Route::post('login', 'Auth\LoginCustomerController@login');
    // Authentication Routes...
    Route::group(['middleware' => 'auth:customer'], function(){
        Route::resource('home', 'HomeCustomerController');
    });   
});

Both in the HomeController controller and in the HomeCustomController I have the same code

function index()
    {
        $me = Auth::guard()->user();
        return response()->json(['me' => $me]);
      }

When I login with the admin with id 1 for example and I access to http://localhost:8000/api/admin/home I respond with the admin data, but if I access http://localhost:8000/api/customer/home , return the client data with id 1.
For both requests access with the token that returns the admin login.

How could I know with the token, which user is logged in? Know if it corresponds to a Customer or an Admin?
Really when an admin tries to access the path of a custommer should not have access and vice versa. How could you check this?

@mtpultz I read all your posts and comments, I think you have the most experience with this package... could you tell us how to solve this security issue when "client" can access "admin" routes?

@chuajose Did you find any solution?

@chuajose @llioor

This seems like more of a StackOverflow question since this issue isn't related to JWT Auth, but a lack of understanding on how Laravel Guards work. In your controller you're using Auth::guard()->user();, but that just uses the default guard equivalent to Auth::guard(null)->user(); which uses the defaults['guard'] in auth.php. You'll want to do something like Auth::guard('customer')->user()' and Auth::guard('user')->user();.

In auth.php you'll have something like:

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

'guards' => [
    'user' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
    'customer' => [
        'driver' => 'jwt',
        'provider' => 'customers',
    ],
    // ...
],

'providers' => [

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

    'customers' => [
        'driver' => 'eloquent',
        'model' => App\Customer::class,
    ],
    // ...
]
````

Which means that for a particular `guard` you will use this `provider`, and within the model associated to the provider you'll have your distinguishing `getJWTCustomClaims` that has a key like `type` or `role` set to be `user` or `customer` so the tokens can be differentiated, and you can drop some logic in middleware to prevent routing to endpoints that don't accept that `type` or `role`.

You can do this a number of different ways, but since you only have the two types I'd use the `LoginController` as a base with a protected method `guard` and member variable `$guard`, create `UserLoginController` and `CustomerLoginController` and inherit the `LoginController`, and change only the `$guard` member variable to be either `user` or `customer` depending on the controller.  Then invoke `$this->guard()` to apply the specific guard during authentication and never hardcode it like `Auth::guard('customer')` directly only use `$this->guard()` so your `LoginController` has only generic authentication logic.

**LoginController Example**

```js
/**
 * Sets the guard to be used during authentication.
 *
 * @var string|null
 */
protected $guard = null;

// All your usually authentication methods using `$this->guard()`

/**
 * Gets the guard to be used during authentication.
 *
 * @return \Illuminate\Contracts\Auth\StatefulGuard
 */
protected function guard()
{
    return Auth::guard($this->guard);
}

CustomerLoginController

/**
 * Sets the guard to be used during authentication.
 *
 * @var string|null
 */
protected $guard = 'customer';

// Any other overrides required specifically for customer authentication

UserLoginController

/**
 * Sets the guard to be used during authentication.
 *
 * @var string|null
 */
protected $guard = 'user';

// Any other overrides required specifically for user authentication

Outside of authentication, I don't like creating multiple classes specific to a type of user and inheriting from a base class since on some projects you might have 6 types of users so I use middleware and an Actor class to access the token claims elsewhere in my application. So if you had an endpoint that was used by one or both types in the middleware that contains generic logic I instantiate a new Actor($token->type) and drop it into application container for reuse in my controllers. So now in the controllers I can using Actor I can get JWT claim information or a model of the authenticated user and it doesn't matter if they are a user or customer, and only when I'm forced to differentiate controllers based on a user type do I create user-specific classes and inherit from a base class.

@mtpultz Martin, I knew I can trust you!!!
Thank you very much for the answer! you are amazing!
Lior.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gandra picture gandra  路  3Comments

mihailo-misic picture mihailo-misic  路  3Comments

CBR09 picture CBR09  路  3Comments

marciomansur picture marciomansur  路  3Comments

lloy0076 picture lloy0076  路  3Comments