Laravel Passport Multiple Authentication using Guards - laravel

Can we use laravel passport with different guards to authenticate APIs for two different types of users.
For example we have driver app for driver user and vendor app for vendor user. Both have their different models Driver and Vendor.
How can we use different guards to authenticate both types of users using Laravel Passport?

I managed to create multiple auths (with laravel/passport) by using a simple middlware.
Step 1: config/auth.php
Add your user classes to providers
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'basic_users', // default
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin_users' => [
'driver' => 'eloquent',
'model' => App\AdminUser::class,
],
'basic_users' => [
'driver' => 'eloquent',
'model' => App\BasicUser::class,
],
],
Clean the cache via CLI
php artisan config:cache
Step 2: Create middleware
php artisan make:middleware AdminUserProvider
Open the newly created middleware in app/Http/Middleware and update the hand method like below
public function handle($request, Closure $next)
{
config(['auth.guards.api.provider' => 'admin_users']);
return $next($request);
}
Step 3: Register your middleware
Add the newly created middleware to $routeMiddleware
protected $routeMiddleware = [
...
'auth.admin' => \App\Http\Middleware\AdminUserProvider::class,
];
and make sure it's at the top of $middlewarePriority
protected $middlewarePriority = [
\App\Http\Middleware\AdminUserProvider::class,
...
];
Step 4: Add middleware to route
Route::group(['middleware' => ['auth.admin','auth:api']], function() {
Step 5: LoginControllers (AdminUserController & BasicUserController)
public function login()
{
$validatedData = request()->validate([
'email' => 'required',
'password' => 'required|min:6'
]);
// get user object
$user = AdminUser::where('email', request()->email)->first();
// do the passwords match?
if (!Hash::check(request()->password, $user->password)) {
// no they don't
return response()->json(['error' => 'Unauthorized'], 401);
}
// log the user in (needed for future requests)
Auth::login($user);
// get new token
$tokenResult = $user->createToken($this->tokenName);
// return token in json response
return response()->json(['success' => ['token' => $tokenResult->accessToken]], 200);
}
In summary:
The login controllers use Eloquent models to get the user object and then log the user in through Auth::login($user)
Then for future requests that need authentication, the new middleware will change the api auth guard provider to the correct class.

Edit: Passport now has support for multiple guard user providers. Please refer the following links for more infos:
Multiple Authentication Guards
Support For Multiple Guards
Old answer (I would not recommend it)
Here is an example of auth.php and api.php to start with
config/auth.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'driver-api' => [
'driver' => 'passport',
'provider' => 'drivers',
],
'vendor-api' => [
'driver' => 'passport',
'provider' => 'vendors',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'drivers' => [
'driver' => 'eloquent',
'model' => App\Driver::class,
],
'vendors' => [
'driver' => 'eloquent',
'model' => App\Vendor::class,
],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
'drivers' => [
'provider' => 'drivers',
'table' => 'password_resets',
'expire' => 60,
],
'vendors' => [
'provider' => 'vendors',
'table' => 'password_resets',
'expire' => 60,
],
],
];
routes/api.php
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
*/
Route::group(['namespace' => 'Driver', 'prefix' => 'driver/v1', 'middleware' => 'auth:driver-api'], function() {
// define your routes here for the "drivers"
});
Route::group(['namespace' => 'Vendor', 'prefix' => 'vendor/v1', 'middleware' => 'auth:vendor-api'], function() {
// define your routes here for the "vendors"
});
You have to modify this files:
File: vendor\laravel\passport\src\Bridge\UserRepository.php
Copy/Paste getUserEntityByUserCredentials to make a duplicate of it and name it getEntityByUserCredentials
Then, in the new duplicated function, find the below:
$provider = config('auth.guards.api.provider');
And Replace it with:
$provider = config('auth.guards.'.$provider.'.provider');
File: vendor\league\oauth2-server\src\Grant\PasswordGrant.php
in : validateUser method add after $username and $password :
$customProvider = $this->getRequestParameter('customProvider', $request);
if (is_null($customProvider)) {
throw OAuthServerException::invalidRequest('customProvider');
}
And this instead of the original line
$user = $this->userRepository->getEntityByUserCredentials(
$username,
$password,
$this->getIdentifier(),
$client,
$customProvider
);
After doing this you'll be able to pass an extra key/value pair to your access token request, like for example:
grant_type => password,
client_id => someclientid
client_secret => somesecret,
username => someuser,
password => somepass,
client_scope => *,
provider => driver-api // Or vendor-api
I hope this will be helpful for you

After spent time I have found that in Laravel 7 there is no custom code required except some configuration. For details please check this answer I have tested & implemented in my projects
Multi Auth with Laravel 5.4 and Passport

You don't necessarily need to change config for each request.
You need a client for each guard. After creating clients by running
passport:install
Make sure you specified provider field in database. It should be same value as auth.providers config.
After creating clients and guards, use following code when creating an access token.
App::clearResolvedInstance(ClientRepository::class);
app()->singleton(ClientRepository::class, function () {
return new ClientRepository(User::CLIENT_ID, null); // Client id of the model
});
Make sure you specified provider in oauth_clients table.

Related

How can I use multiple jwt authentication for laravel 10?

I have created two guards.Admin and Manager. To create authentication for admin I have followed https://blog.pusher.com/laravel-jwt/ this blog and it worked well. For my 2nd guard authentication middleware route manager not working. It says Manager not found.So I put only admin in group route and for manager I have declared the guard inside of auth method but It still not working. It can't find my model to store so I had to post the row data inside phpmyadmin manually and checked the login if it works or not but it's still not working. Returns the error part.
Api.php
Route::post('/manager/register', [App\Http\Controllers\ManagerUserController::class,'register']);
Route::post('/manager/login', [App\Http\Controllers\ManagerUserController::class,'authenticate']);
Route::get('open', [ManagerDataController::class,'open']);
Route::group(['middleware' => ['jwt.verify']], function() {
Route::get('user', [ManagerUserController::class,'getAuthenticatedUser']);
Route::get('closed', [ManagerDataController::class,'closed']);
});
Route::group(['middleware'=>'admin'],function($router){
Route::post('/admin/register', [App\Http\Controllers\AdminUserController::class,'register']);
Route::post('/admin/login', [App\Http\Controllers\AdminUserController::class,'authenticate']);
Route::get('open', [AdminDataController::class,'open']);
Route::group(['middleware' => ['jwt.verify']], function() {
Route::get('user', [AdminUserController::class,'getAuthenticatedUser']);
Route::get('closed', [AdminDataController::class,'closed']);
});
managerController:
public function authenticate(Request $request)
{
$credentials = $request->only('username', 'password');
if (! $token = auth()->guard('manager')->attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 400);
}
return response()->json(compact('token'));
}
auth.php
'defaults' => [
'guard' => 'admin',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'jwt',
'provider' => 'admins',
],
'manager' => [
'driver' => 'jwt',
'provider' => 'managers',
],
]
Another problem is if I set web for default guard then admin guard not working but if I put admin as default guard then it works. It this work like this then how can I use multi jwt authentication cause theres only single default guard exists!

How to use custom guard and avoid the default guard

This is how my config/auth.php looks like.
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'jwt',
'provider' => 'admins',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'provider' => App\Models\Admin::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
And this is how I defined the guard in my controller:
if (! $token = JWTAuth::attempt($credentials,['guard' => 'admin'])) {
return response()->json(['error' => 'invalid_credentials'], 400);
}
Problem is I have customized admin guard and defined the model that I wanna use but It's looking for default guard and when it does it avoids the custom guard and use default guards providers User model(Which I want to avoid) . To get rid of it I removed the default api guard and now it's saying api guard not defined. If I define I only can use one guard and can't use my customize guard. How can I avoid the default guard and use other two guard ?
In admin controller,
public function __construct(){
\Config::set('auth.defaults.guard','admin');
}
In manager controller,
public function __construct(){
\Config::set('auth.defaults.guard','manager');
}
This solved the problem!
Define a new guard in the guards array of the config/auth.php file. For example, you can add a custom guard like this:
'guards' => [
// ...
'custom' => [
'driver' => 'session',
'provider' => 'users',
],
],
Define a new provider in the providers array of the config/auth.php file. This provider should be responsible for retrieving and authenticating your users. For example, you can add a custom provider like this:
'providers' => [
// ...
'custom' => [
'driver' => 'eloquent',
'model' => App\Models\CustomUser::class,
],
],
Create a new middleware that sets the guard for your custom authentication. For example, you can create a CustomAuthenticate middleware like this:
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate;
class CustomAuthenticate extends Authenticate
{
protected function authenticate($request, array $guards)
{
if (!empty($guards) && $guards[0] === 'custom') {
return auth()->guard('custom')->check();
}
parent::authenticate($request, $guards);
}
}
Apply the new middleware to your routes or controllers. For example, you can add the CustomAuthenticate middleware to your routes like this:
Route::middleware(['auth:custom'])->get('/custom', function () {
return view('custom');
});
or alternatively you can define the guard that you want only to be used within the controller by setting this in your construct method.
public function __construct()
{
$this->middleware('auth:custom');
}
To set the users guard upon logging in you can do within your login post request within your Login controller.
if (Auth::guard('custom')->attempt($credentials)) {
// Authentication passed...
return redirect()->intended('dashboard');
}
When youre logging in to set the guard with JWTAuth you can do;
$token = JWTAuth::guard('custom')->attempt($credentials);

Laravel custom login username and password mapping

I am upgrading a legacy application.
I am using Laravel 8 without ORM with DB first approach since the DB is already there.
I want to use
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('shop');
}
When I am configuring auth.php I saw inside the providers 'driver' => 'database' was commented. I want to use auth.php providers like following
'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\Models\User::class,
// ],
'users' => [
'driver' => 'database',
'table' => 'shop_users',
],
]
Is there any way I can map the user name password like following
'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\Models\User::class,
// ],
'users' => [
'driver' => 'database',
'table' => 'shop_users',
'username' => 'email', //Mapping auth username to table column
'password' => 'password' //Mapping auth password to table password
],
]
Make sure this model call is extended Authenticatable
we need to add a new function inside app/Http/Auth/LoginController.php like below:
public function username(){
return 'email'; // this string is column of shop_users table which we are going use for login
}

Multiple auth guards in Lumen framework

Please, can anybody explain how to implement multiple authentication guards in Lumen framework? Currently, I have two authenticatable models: Users and Clients. I'm using a custom implementation of JWT. A User has client_id and user_id fields in their token payload. While a Client only has client_id. Based on this I need to determine who came to me: client, user or guest (without a token).
auth.php
'guards' => [
'client' => [
'driver' => 'token',
'provider' => 'clients',
],
'user' => [
'driver' => 'token',
'provider' => 'users',
],
],
'providers' => [
'clients' => [
'driver' => 'eloquent',
'model' => App\Client::class,
],
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
],
AuthServiceProvider.php
public function boot()
{
$this->app['auth']->viaRequest('token', function ($request) {
$access_token = HelperClass::getTokenFromHeader($request->headers->get('Authorization'));
if ($access_token) {
$tokendata = JWT::decode($access_token, getenv('TOKEN_SECRET'), array('HS256'));
if ($tokendata->user_id) {
return User::find($tokendata->user_id);
}
return Client::find($tokendata->client_id);
}
});
}
routes.php
$app->get('/api/{item_id:\d+}', ['middleware' => 'auth:user', 'uses' => 'App\Http\Controllers\ItemController#get']);
I want to allow only Users to access this route, but Clients successfully pass this middleware too: Auth::check() returns true and Auth::user() returns an instance of App\Client 😕
Another situation: what if for some routes I want to allow both: clients and users. For another routes - guests, clients and users.

Can Anyone Explain Laravel 5.2 Multi Auth with Example

I am trying to authenticate users and admin form user table and admin table respectively. I am using the User model as provided by laravel out of the box and created the same for Admin. I have added a guard key and provider key into auth.php.
Guards
'guards' => [
'user' =>[
'driver' => 'session',
'provider' => 'user',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
],
],
Providers
'providers' => [
'user' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
]
],
Routes
Route::group(['middleware' => ['web']], function () {
// Login Routes.
Route::get('/admin/login','AdminAuth\AuthController#showLoginForm');
Route::post('/admin/login','AdminAuth\AuthController#login');
Route::get('/admin/logout','AdminAuth\AuthController#logout');
// Registration Routes.
Route::get('admin/register', 'AdminAuth\AuthController#showRegistrationForm');
Route::post('admin/register', 'AdminAuth\AuthController#register');
Route::get('/admin', 'AdminController#index');
});
I have created a directory called AuthAdmin where Laravel's default AuthController.php and PasswordController.php files are present. (Namespace Modified accordingly)
First of all, in Laravel's docs mentioned that how to specify custom guard while authenticating like this which isn't working.
There's another method mentioned in Laravel's docs to use a guard which is not working too.
It would be beneficial if someone could resolve the issues and correct me if I am wrong.
After lots of digging and lots of questions & answers I have finally managed to work Laravel 5.2 Multi Auth with two table, So I'm writing Answer of my own Question.
How to implement Multi Auth in Laravel 5.2
As Mentioned above.
Two table admin and users
Laravel 5.2 has a new artisan command.
php artisan make:auth
it will generate basic login/register route, view and controller for user table.
Make a admin table as users table for simplicity.
Controller For Admin
app/Http/Controllers/AdminAuth/AuthController
app/Http/Controllers/AdminAuth/PasswordController
(note: I just copied these files from app/Http/Controllers/Auth/AuthController here)
config/auth.php
//Authenticating guards
'guards' => [
'user' =>[
'driver' => 'session',
'provider' => 'user',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
],
],
//User Providers
'providers' => [
'user' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
]
],
//Resetting Password
'passwords' => [
'clients' => [
'provider' => 'client',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admin',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
],
route.php
Route::group(['middleware' => ['web']], function () {
//Login Routes...
Route::get('/admin/login','AdminAuth\AuthController#showLoginForm');
Route::post('/admin/login','AdminAuth\AuthController#login');
Route::get('/admin/logout','AdminAuth\AuthController#logout');
// Registration Routes...
Route::get('admin/register', 'AdminAuth\AuthController#showRegistrationForm');
Route::post('admin/register', 'AdminAuth\AuthController#register');
Route::get('/admin', 'AdminController#index');
});
AdminAuth/AuthController.php
Add two methods and specify $redirectTo and $guard
protected $redirectTo = '/admin';
protected $guard = 'admin';
public function showLoginForm()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
return view('admin.auth.login');
}
public function showRegistrationForm()
{
return view('admin.auth.register');
}
it will help you to open another login form for admin
creating a middleware for admin
class RedirectIfNotAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = 'admin')
{
if (!Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
}
register middleware in kernel.php
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\RedirectIfNotAdmin::class,
];
use this middleware in AdminController
e.g.,
middleware('admin');
}
public function index(){
return view('admin.dashboard');
}
}
That's all needed to make it working and also to get json of authenticated admin use
`Auth::guard('admin')->user()`
**Edit - 1**
We can access authenticated user directly using
`Auth::user()`
but if you have two authentication table then you have to use
Auth::guard('guard_name')->user()
for logout
Auth::guard('guard_name')->user()->logout()
for authenticated user json
Auth::guard('guard_name')->user()
##Edit 2
Now you can download Laravel 5.2 Multiauth implemented Project http://imrealashu.in/code/laravel/multi-auth-with-laravel-5-2-2/
In case this helps anyone, and this may just be due to my lack of understanding of middleware, here's what I had to do to get this working (in addition to the steps taken by #imrealashu)...
In route.php:
Route::get('/admin', [
'middleware' => 'admin',
'uses' => 'AdminController#index'
]);
This is in the web middleware group. Before this I tried putting it in a separate admin middleware group and even in an auth:admin group but this didn't work, it only worked for me when I specified the middleware as admin on the route itself. I have no idea why this is but I hope it saves others from pulling their hair out like I did.
It's very easy in laravel 5.6. Just go to config/auth.php and add this line in providers array:
'admins' => [
'driver' => 'database',
'table' => 'admin_table'
]
Note that we used database for driver not eloquent.
Now add this to guards array:
'admin_guard' => [
'driver' => 'session',
'provider' => 'admins'
]
Now we're done! Use this when working with admins table:
Auth::guard('admin_guard')->User();
Cheers.

Resources