Lravel 5.4: JWT API with multi-auth on two tables one works the other not

爷,独闯天下 提交于 2019-11-29 15:31:40

问题


I am using...

  • Laravel 5.4
  • tymon/jwt-auth : 1.0.0-rc.2

I have application with two authentications API one is customers and the other is drivers each one has it's own table.

now let me describe shortly JWT package installation and the updates I did on it.

  • I installed the package as described in the JWT documents exactly.
  • Now comes to the quick start here I updated two Models one is the User and the second Driver.
  • Comes here to the Configure Auth guard again I used the configuration for the two guards let me show a snapshot of my auth.php.

    'defaults' => [
        'guard'     => 'api',
        'passwords' => 'users',
    ], 
    
    'guards' => [
            'web' => [
                'driver'   => 'session',
                'provider' => 'users',
            ],
    
            'api' => [
                'driver'   => 'jwt',
                'provider' => 'users',
            ],
    
            'driver'    => [
                'driver'   => 'session',
                'provider' => 'drivers',
            ],
    
            'driver-api' => [
                'driver'   => 'jwt',
                'provider' => 'drivers',
            ],
        ],
    
    'providers' => [
            'users'     => [
                'driver' => 'eloquent',
                'model'  => App\Models\User::class,
            ],
    
            'drivers'   => [
                'driver' => 'eloquent',
                'model'  => App\Models\Driver::class,
            ],
        ],
    
  • Now continue the application with authentication routes here is my Routes for the two Models

Here is the User and Driver Routes

Route::group( [
    'prefix'     => 'auth',
    'middleware' => 'api'
], function () {
    .......
});

Route::group( [
    'prefix'     => 'driver',
    'middleware' => 'api'
], function () {
   .......
});
  • Now comes the AuthController

in the JWT documentation the construct is writing like that.

public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

I found some article that suggest to make it something like this to switch between the two models we have.

so here with my controller looks like now.

public function __construct() {
        $this->user   = new User;
        $this->driver = new Driver;
    }

public function userLogin( Request $request ) {
        Config::set( 'jwt.user', 'App\Models\User' );
        Config::set( 'auth.providers.users.model', User::class );
        $credentials = $request->only( 'email', 'password' );
        $token       = null;
        try {
            if ( $token = $this->guard()->attempt( $credentials ) ) {
                return response()->json( [
                    'response' => 'error',
                    'message'  => 'invalid_email_or_password',
                ] );
            }
        } catch ( JWTAuthException $e ) {
            return response()->json( [
                'response' => 'error',
                'message'  => 'failed_to_create_token',
            ] );
        }

        return response()->json( [
            'response' => 'success',
            'result'   => [
                'token'   => $token,
                'message' => 'I am front user',
            ],
        ] );
    }

    public function driverLogin( Request $request ) {
        Config::set( 'jwt.user', 'App\Models\Driver' );
        Config::set( 'auth.providers.users.model', Driver::class );
        $credentials = $request->only( 'email', 'password' );
        $token       = null;
        try {
            if ( ! $token = $this->guard()->attempt( $credentials ) ) {
                return response()->json( [
                    'response' => 'error',
                    'message'  => 'invalid_email_or_password',
                ] );
            }
        } catch ( JWTAuthException $e ) {
            return response()->json( [
                'response' => 'error',
                'message'  => 'failed_to_create_token',
            ] );
        }

        return response()->json( [
            'response' => 'success',
            'result'   => [
                'token'   => $token,
                'message' => 'I am driver user',
            ],
        ] );
    }

public function me() {
        return response()->json( $this->guard()->user() );
    }

public function logout() {
        $this->guard()->logout();

        return response()->json( [ 'message' => 'Successfully logged out' ] );
    }

public function refresh() {
        return $this->respondWithToken( $this->guard()->refresh() );
    }

protected function respondWithToken( $token ) {
        return response()->json( [
            'access_token' => $token,
            'token_type'   => 'bearer',
            'expires_in'   => $this->guard()->factory()->getTTL() * 60
        ] );
    }

public function guard() {
        return Auth::guard();
    }  

Now with is happening and the problems I faced

Now the driver api is working as login only Ex.

localhost:8000/api/driver/login Working fine

but when try to get the driver user id like this

localhost:8000/api/driver/me it return empty array

Second Issue comes.

the use login from the interface for Ex. http://localhost:8000/login it returns back to the login screen without any errors becouse the login information is right but the defaults in the auth.php is 'guard'=>'api' if I change it to 'guard'=>'web' it do the login correctly.

even the User API for Ex. localhost:8000/api/auth/login always return

{
    "response": "error",
    "message": "invalid_email_or_password"
}

Update

I solved half the way I updated the AuthController to be something like this.

public function __construct() {
        if ( Request()->url() == '/api/driver/me' ) {
            $this->middleware( 'auth:driver-api', [ 'except' => [ 'login' ] ] );
        } elseif ( Request()->url() == '/api/customer/me' ) {
            $this->middleware( 'auth:api', [ 'except' => [ 'login' ] ] );
        }
    }

and the login function to be something like this.

public function login() {
        if ( Request()->url() == '/api/driver' ) {
            Config::set( 'auth.providers.users.model', Driver::class );
            $credentials = request( [ 'email', 'password' ] );

            if ( ! $token = auth()->attempt( $credentials ) ) {
                return response()->json( [ 'error' => 'Unauthorized' ], 401 );
            }

            return $this->respondWithToken( $token );
        }

        Config::set( 'auth.providers.users.model', User::class );
        $credentials = request( [ 'email', 'password' ] );

        if ( ! $token = auth()->attempt( $credentials ) ) {
            return response()->json( [ 'error' => 'Unauthorized' ], 401 );
        }

        return $this->respondWithToken( $token );

    }

but still have problem in auth.php here it is

'defaults' => [
        'guard'     => 'driver-api',
        'passwords' => 'users',
    ], 

here I need to switch the 'guard'=>'api' to be 'guard'=>'driver-api' in case if URL request is localhost:8000/api/driver/login and 'guard'=>'api' in case if URL request is localhost:8000/api/customer/login any way to do this.

Update 2

Here is the driver Model

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

class Driver extends Authenticatable implements JWTSubject {
    protected $guard = 'driver';

    protected $fillable = [
        ...
        'email',
        'password',
        ...
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    public function getJWTIdentifier() {
        return $this->getKey();
    }

    public function getJWTCustomClaims() {
        return [];
    }
}

and the User Model

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

class User extends Authenticatable implements JWTSubject {
    use Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    public function getJWTIdentifier() {
            return $this->getKey();
        }

        public function getJWTCustomClaims() {
            return [];
        }

I need some help, Ideas please.


回答1:


There is no need to change the providers in config/auth.php.

You can change the __construct function in each of your controllers as follows. So that jwt know which model to authenticate.

DriverController

function __construct()
{
    Config::set('jwt.user', Driver::class);
    Config::set('auth.providers', ['users' => [
            'driver' => 'eloquent',
            'model' => Driver::class,
        ]]);
}



回答2:


My example when i used multi auth with jwt

I have 2 models : 1. users 2. admins

the routes :

Route::post('auth/userlogin', 'ApiController@userLogin');
Route::post('auth/adminlogin', 'ApiController@adminLogin');

the controller:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests;
use Config;
use JWTAuth;
use JWTAuthException;
use App\User;
use App\Admin;

class ApiController extends Controller
{

    public function __construct()
    {
        $this->user = new User;
        $this->admin = new Admin;
    }

    public function userLogin(Request $request){
        Config::set('jwt.user', 'App\User'); 
        Config::set('auth.providers.users.model', \App\User::class);
        $credentials = $request->only('email', 'password');
        $token = null;
        try {
            if (!$token = JWTAuth::attempt($credentials)) {
                return response()->json([
                    'response' => 'error',
                    'message' => 'invalid_email_or_password',
                ]);
            }
        } catch (JWTAuthException $e) {
            return response()->json([
                'response' => 'error',
                'message' => 'failed_to_create_token',
            ]);
        }
        return response()->json([
            'response' => 'success',
            'result' => [
                'token' => $token,
                'message' => 'I am front user',
            ],
        ]);
    }

    public function adminLogin(Request $request){
        Config::set('jwt.user', 'App\Admin'); 
        Config::set('auth.providers.users.model', \App\Admin::class);
        $credentials = $request->only('email', 'password');
        $token = null;
        try {
            if (!$token = JWTAuth::attempt($credentials)) {
                return response()->json([
                    'response' => 'error',
                    'message' => 'invalid_email_or_password',
                ]);
            }
        } catch (JWTAuthException $e) {
            return response()->json([
                'response' => 'error',
                'message' => 'failed_to_create_token',
            ]);
        }
        return response()->json([
            'response' => 'success',
            'result' => [
                'token' => $token,
                'message' => 'I am Admin user',
            ],
        ]);
    }
}

I hope that's help you .




回答3:


First let me thank you @AmrAbdelRahman for you efforts and your time.

My problem was the application always using my default authentication "guard" as my default looks like that

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

so every time I try to authenticate the other user which was the driver it fails during the default authenticate is api and the it should using driver in this case.

what I did in my case was making a switcher in my App\Providers\AppServiceProvider under the boot here is how it looks like

$this->app['router']->matched(function (\Illuminate\Routing\Events\RouteMatched $e) {
            $route = $e->route;
            if (!array_has($route->getAction(), 'guard')) {
                return;
            }
            $routeGuard = array_get($route->getAction(), 'guard');
            $this->app['auth']->resolveUsersUsing(function ($guard = null) use ($routeGuard) {
                return $this->app['auth']->guard($routeGuard)->user();
            });
            $this->app['auth']->setDefaultDriver($routeGuard);
        });

Now in my case if the $guard =api it will read that guard and act correctly and if it's driver it will use the new guard and act as expected. Hope this will help some one in future.



来源:https://stackoverflow.com/questions/49212086/lravel-5-4-jwt-api-with-multi-auth-on-two-tables-one-works-the-other-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!