How to use custom guard and avoid the default guard - laravel

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);

Related

Why does custom guard get logged out on page reload/redirect?

I created a new guard for logging in custom type of users. I can login users with Auth::attempt('webguest'), but login does not persist over page reloads. It works only when I use web-guard. See for example my two test routes:
Route::get('test1', function () {
\Auth::guard('webguest')->attempt([
'id' => 5,
'password' => 2222
]);
// here dump(\Auth::guard('webguest')->check()) shows true
return \Redirect::to('/test2');
});
Route::get('test2', function () {
return \Auth::guard('webguest')->check() ? "guest account logged in" : "guest not logged in";
// dumps not logged in
});
My custom guard:
config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'webguest' => [
'driver' => 'session',
'provider' => 'guests'
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'guests' => [
'driver' => 'eloquent',
'model' => App\Models\Guest::class
],
]
Both Guest and User models extend Illuminate\Foundation\Auth\User as Authenticatable.
How can I make login with the custom guard persist like with web guard?
I've found the root cause.
In my Guest model the "username" used for login is the id, and there was an overriden method in the model:
public function getAuthIdentifier() {
return $this->user->getKey();
}
I removed it and added the following instead:
public function getAuthIdentifierName()
{
return 'id';
}
Now the custom guard works like a charm.

Laravel check if user is authenticated regardless of guard

I have multiple guards for my site e.g.:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'recruiter' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'users',
],
],
Is there a method to check if a user is simply signed in i.e. authenticated regardless of any guards?
Currently I have to do something like this:
if (Auth::guard('web')->check() ||
Auth::guard('recruiter')->check() ||
Auth::guard('admin')->check()) {
}
I don't believe there is a single method for checking against all guards. Have you considered a middleware which encapsulates your auth guard checks so that at least then you don't have the checks peppered throughout your code base?
As others have stated this isn't a feature provided by Laravel. You will probably have to create something in user land code for this feature. I've created something that might work for your specific use case:
namespace Your\Namespace;
use Illuminate\Support\Facades\Auth;
class Guard
{
public static function checkAny(...$guards)
{
foreach($guards as $guard) {
if (Auth::guard($guard)->check()) {
return true;
}
}
return false;
}
}
if (\Your\Namespace\Guard::checkAny('web', 'admin','recruiter')) {
// don't show captcha
}

Laravel Passport Multiple Authentication using Guards

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.

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.

how to protect a route with one of two auth middlewares in laravel 5.2

I am using multiple auth in my laravel project and it works fine:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admins' =>[
'driver' => 'session',
'provider' => 'admins',
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
],
But I have Problem with protecting some of my routes. I want these routes to be accessed by two type of authentication guards. if 'admins' guard user is logged in OR if 'users' guard user is logged in, I want to be able to access the route. now my route is like this:
Route::group(['middleware' =>'auth'], function () {
Route::get('/add/tutorial',[
'uses'=>'AddTutorialController#getAddTutorialIndex',
'as'=>'frontend.add.tutorial'
]);
});
and with this, just users from my 'users' guard can access this route.
I want to access maybe something like this:
Route::group(['middleware' =>['auth' OR 'auth:admins']],...
Is there any way to let both of auth guards access the route?? which kind of middleware group I should use?
If you could add an additional parameter to \app\Http\Middleware\Authenticate.php like:
public function handle($request, Closure $next, $guard = null, $default = false)
{
if($guard && $default) {
// check for specified $guard OR default guard
$authorized = Auth::guard($guard)->guest() || Auth::guard(null)->guest();
} else {
// use specified guard ( or null if non given )
$authorized = Auth::guard($guard)->guest();
}
if ($authorized) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
With that you can specifiy an additional parameter in a route group :
Route::group(['middleware' => 'auth:admins,true'], function() {
Route::get('/', function () {
dd('inside');
});
});
If you use auth:admins,true then the default guard and the specified guard (admins) would be checked with OR.
If you only use auth the default guard will be used. If you use auth:admins
the admins guard will be used only.
This is just a quick example. You can adjust it to your needs, or even write an own middleware.
i found 3 ways, if you can found useful:-
First Way:-
routes.php
Route::group(['middleware' =>['auth', 'auth:admins']], function () {
Route::get('/add/tutorial',[
'uses'=>'AddTutorialController#getAddTutorialIndex',
'as'=>'frontend.add.tutorial'
]);
});
Second Way:-
routes.php
Route::get('/add/tutorial',[
'uses'=>'AddTutorialController#getAddTutorialIndex',
'as'=>'frontend.add.tutorial'
])->middleware(['auth', 'auth:admins']);
Third Way:-
routes.php
Route::get('/add/tutorial',[
'uses'=>'AddTutorialController#getAddTutorialIndex',
'as'=>'frontend.add.tutorial'
])
yourcontroller.php
class YourController extends Controller{
public function __construct(){
$this->middleware(['auth', 'auth:admins']);
}
}

Resources