Laravel Passport multi auth always falling back to my default guard - laravel-8

I'm currently moving an app from Laravel/lumen 6 to Laravel/Lumen8.
Previously I was using this package https://github.com/sfelix-martins/passport-multiauth to handle multi auth.
Now that Laravel Passport is handling multiple auth natively I have switched to Passport 10.
I have updated my config/auth.php file with all my guards and providers
return [
'defaults' => [
'guard' => 'users',
'passwords' => 'users',
],
'guards' => [
'users' => [
'driver' => 'passport',
'provider' => 'users',
],
'dashboardUsers' => [
'driver' => 'passport',
'provider' => 'dashboardUsers',
],
'retailers' => [
'driver' => 'passport',
'provider' => 'retailers',
]
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\User::class,
],
'dashboardUsers' => [
'driver' => 'eloquent',
'model' => \App\DashboardUser::class,
],
'retailers' => [
'driver' => 'eloquent',
'model' => \App\DashboardUser::class,
],
],
...
];
I'm having my defaults middlewares in boostrap/app.php like this:
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
]);
The Authenticate Middleware is the one defined in default Lumen package:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Factory as Auth;
use Illuminate\Auth\AuthenticationException;
class Authenticate
{
/**
* The authentication guard factory instance.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* 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 = null)
{
if ($this->auth->guard($guard)->guest()) {
throw new AuthenticationException();
}
return $next($request);
}
}
If I use my default guard (users) everything is working fine. But if I try to use one of my 2 other guards like this :
$router->group(['middleware' => ['auth:dashboardUsers', 'scope:admin-retailers,manage-retailers']], function () use ($router) {
$router->get('/dashboard/retailers/getRetailers', 'RetailerController#getRetailersByUser');
});
The result is really weird since my guard seems to be correctly defined (dashboardUsers), but it's still falling back to my default provider (so my user can't be found of course).
The only fix I could find is to update my Authenticate middleware like this:
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
throw new AuthenticationException();
}
config(['auth.guards.users.provider' => $guard]);
return $next($request);
}
This way I'm redefining my config to use the correct provider. But that feels hacky and I think I'm missing a point somewhere in my configuration. Since everything is defined in my auth.php file I shouldn't have to redefine my provider in my middleware.
Any idea what could be the problem?
Thanks for the help.

Related

Spartie - User does not have the right roles. Laravel multi guard

I'm getting this error "message": "User does not have the right roles.", "exception": "Spatie\Permission\Exceptions\UnauthorizedException", I am using spatie package https://github.com/spatie/laravel-permission
This error occurs anytime I try accessing an api route protected with ['role:admin'] middleware. I'm using passport for API authentication and Spatie for ACL. I used multi guards. Everything works fine without the spatie middleware but I want to protect certain routes based on user roles
This is my config/auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'admin' => [
'driver' => 'eloquent',
'model' => Admin::class,
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
This is my kernel.php
protected $routeMiddleware = [
.......
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
When registering a user, I assign the role of admin like this in my AuthController. The 'admin' role gets assigned to the User successfully but I still get "User does not have the required role" whenever I want to access an [role:admin] protected route.
public function registration(Request $request)
{
...........
$user = User::create($data);
$user->assignRole('admin');
$response = [];
$response['token'] = $user->createToken('api-application')->accessToken;
$response['user'] = [$user, $organization];
return response()->json($response, 202);
}
This is my user model
.......
protected $guard_name = 'api';
protected $table = 'users';
protected $fillable = [
'firstname',
'lastname',
'email',
'password',
'organization_id',
];
In my Controller VisitorsController I added the spatie role middleware to protect like this
public function __construct()
{
$this->middleware(['role:admin']);
}
public function index()
{
$visitor = Visitor::all();
return VisitorsResource::collection($visitor);
}
And in my api.php I have this
Route::middleware('auth:api')->prefix('V1')->group(function () {
........
Route::apiResource('/visitors', VisitorsController::class);
});
.........
Route::post('/register', [AuthController::class, 'registration'])->name('registration');
Have I provided enough information? And please what I'm I doing wrong?

Laravel 7 Passport with different and multiple Models

I am currently developing an API with Laravel 7 and I want to authenticate users coming from different applications with Passport : 3 applications communicate with the API, so 3 different user tables.
It's working fine with the User model provided but when I use my own model, I can't login : it responds "Bad credentials" with the good credentials.
So my question is : Is it possible to use a different model from User provided and also multiple model at the same time for each APP/Client ?
Here is a diagram of the structure I want : Diagram
My App\User :
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable{
use Notifiable, HasApiTokens;
....
}
Here is my login function :
public function login(Request $request)
{
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string'
]);
$credentials = request(['email', 'password']);
if(!Auth::attempt($credentials))
return response()->json([
'message' => 'Bad credentials'
], 401);
$user = $request->user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
$token->expires_at = Carbon::now()->addHours(1);
$token->save();
return response()->json([
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse(
$tokenResult->token->expires_at
)->toDateTimeString()
]);
}
}
My config/auth
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'userstest',
],
'api' => [
'driver' => 'passport',
'provider' => 'userstest',
'hash' => false,
],
], 'providers' => [
'userstest' => [
'driver' => 'eloquent',
'model' => App\User::class,
],

Simple API Call gets redirected

I'm creating a Nextcloud-App (using Nextcloud 20). I want a simple call to my external service. Due to CSP restrictions (set by default by netxcloud), I simply can't.
Everytime I request my URL, using window.OC.generateUrl('/apps/[MYAPP]/video/trim'); I get a redirect response (code 302), instead of a success (code 200). What did I miss?
I registered my route:
// [MYAPP]/appinfo/routes.php
return [
'routes' => [
['name' => 'video#trim', 'url' => '/trim', 'verb' => 'POST']
]
];
I've build my controller:
// [MYAPP]/lib/controller/VideoController.php
namespace OCA\[MYAPP]\Controller;
use OCP\IRequest;
use GuzzleHttp\Client;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
class VideoController extends Controller
{
/**
* #NoAdminRequired
*/
public function trim(string $title, string $content)
{
$client = new Client([
'base_uri' => 'http://localhost:3000/push',
'timeout' => 2.0,
]);
$response = $client->request('POST', ['json' => $content]);
return new DataResponse(['foo' => 'bar']);
}
}
I'm POSTing my request to it. In console I see a redirect to Location http://localhost:9000/apps/dashboard/.
// js
const opts = {}; // my payload
const url = window.OC.generateUrl('/apps/[MYAPP]/video/trim');
fetch(url, {method: 'post', body: JSON.stringify(opts)})
.catch((err) => console.error(err))
.then((response) => response.json())
.then((data) => console.log(data));
I finally found the problem in routes.php!
Since I'm generating the URL for /apps/[MYAPP]/video/trim, the url in routes.php should look like /video/trim instead of /trim.
// [MYAPP]/appinfo/routes.php
return [
'routes' => [
['name' => 'video#trim', 'url' => '/video/trim', 'verb' => 'POST']
]
];

Method Illuminate\Auth\RequestGuard::attempt does not exist

I am new to both laravel and lumen.
I was creating a login api with oauth2.0 in lumen 5.6, i have installed passport and generated token.
Below is my login controller function and it is working fine. It returns token.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
//use Illuminate\Support\Facades\DB;
use App\User;
use Auth;
public function login(Request $request)
{
global $app;
$proxy = Request::create(
'/oauth/token',
'post',
[
'grant_type' => env('API_GRAND_TYPE'),
'client_id' => env('API_CLIENT_ID'),
'client_secret' => env('API_CLIENT_SECRET'),
'username' => $request->username,
'password' => $request->password,
]
);
return $app->dispatch($proxy);
}
Since i have to check user status apart from username and password, i need to check the user credential first. so i do like this.
public function login(Request $request)
{
$credentials = $request->only('username', 'password');
if (Auth::attempt($credentials)) {
return ['result' => 'ok'];
}
return ['result' => 'not ok'];
}
Here i am getting this error.
Method Illuminate\Auth\RequestGuard::attempt does not exist.
So i tried Auth::check instead of Auth::attempt.
Now there is no error but it always return false even though the credentials are valid.
I searched a lot for a solution but i didn't get.
The function guard is only available for routes with web middleware
public function login() {
if(Auth::guard('web')->attempt(['email' => $email, 'password' => $password], false, false)) {requests
// good
} else {
// invalid credentials, act accordingly
}
}
Changing default guard to "web" fixed my problem.
config/auth.php:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
using jwt it's worked for me
1-Auth::attempt($credentials)
just replace line 1 to line 2
2-$token = Auth::guard('api')->attempt($credentials)

CakePHP Error: Class App\Controller\AuthComponent not found

I'm working in CakePHP 3.2 and writing an admin panel where only admin can login.
There is a separate table admins to store admin credentials. There is users table also which is used for users to register/login from main application.
I have to use admins table to login in admin panel.
What I have done is.
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Event\Event;
class AppController extends Controller
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => 'Admins',
'action' => 'login',
'plugin' => 'Admins'
],
'loginRedirect' => [
'controller' => 'ServiceRequests',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Admins',
'action' => 'login'
],
'authenticate' => [
'Form' => [
'userModel' => 'Admin',
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
]
]);
}
public function beforeRender(Event $event)
{
if (!array_key_exists('_serialize', $this->viewVars) &&
in_array($this->response->type(), ['application/json', 'application/xml'])
) {
$this->set('_serialize', true);
}
}
}
AdminsController.php
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\Event;
use App\Controller\AuthComponent;
/**
* Admins Controller
*
* #property \App\Model\Table\AdminsTable $Admins
*/
class AdminsController extends AppController
{
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow('add');
// Pass settings in using 'all'
$this->Auth->config('authenticate', [
AuthComponent::ALL => ['userModel' => 'Members'],
'Basic',
'Form'
]);
}
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
public function logout()
{
return $this->redirect($this->Auth->logout());
}
}
But this is not working. and gives Error: Class App\Controller\AuthComponent' not found
Also I want to restrict access to all controllers and actions without login. Thats why there is no $this->Auth->allow() in AppsController.php
use Cake\Controller\Component\AuthComponent;