Laravel 5.7 + Spatie Permissions + JWT auth - php

I'm setting up a REST API using Laravel 5.7. To validate authentication I JWT-auth and for permissions and roles I use Spatie.
My problem: when trying to link a role to a user I get the following error
Spatie \ Permission \ Exceptions \ RoleDoesNotExist
There is no role named admin.
The role do exist in the database:
This is how I'm trying to assign a role to the user:
$user = User::findOrFail(1);
$user->assignRole('admin');
As I'm new to Laravel I'm not sure if it's relevant, but setting the JWT I had to change the driver of the guard in the config/auth.php to jwt
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
I can't see what I'm doing wrong. I added the roles and then tried to add the role to a user.

Check your app namespace. If you updated it from App, be sure to update it in config/auth.php.
On the other hand, if you didn't update the App namespace, try clearing your cache and re-seed the database tables.
php artisan config:cache
php artisan cache:clear
Also check the user model if you have protected $guard_name = 'api'; in there.
Hope this helps. Cheers!

I'm assuming you've manually added the roles to the database. The roles are cached and it causes issues if you don't use the built in create methods.
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
$role = Role::create(['name' => 'writer']);
$permission = Permission::create(['name' => 'edit articles']);
From the GitHub docs
You will need to manually clear the cache with php artisan cache:clear.
The better way to do this is either to use a seeder (for permanent roles) or Tinker to run the code to create roles and permissions, which will trigger the cache to be cleared.

Guard has been changed from the web to api but still try to find web from the database
assign a role like this.
$roleToAssign = Role:: findByName('administrator', 'api');
$user->assignRole($roleToAssign);
#Link

If none of other answers solved your problem
Check that you are using queue
if so
restart the queue

Related

Laravel Passport - Multiple guards issue

Thank you in advance,
I want multiple laravel passport guards as my system has 2 user types, 1) Admin, 2) Normal User.
for both, I have separate routes and authentication modules(Login, register, logout, etc). so I need a separate passport guard for the API authentication. a few of the codes I added as below
config/auth.php looks like below
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
'api-admin' => [
'driver' => 'passport',
'provider' => 'admins',
'hash' => false,
]
],
Here i defined 2 guards for admin and user
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'table' => App\Models\Admin::class,
],
],
Here i defined 2 providers for admin and user
Now i am creating token like
$tokenResult = $user->createToken('TOKEN_DEMO');
$token = $tokenResult->token;
$token->save();
$accessToken = $tokenResult->accessToken;
It is generating well as expected for admin user with user_id = 1 (As an example consider user_id = 1)
this is about generating token for the admin user
the same way normal user logged in and generating token as same as above then this will also generate the token for the user with user_id = 1 in oauth_clients table
The table looks like as mentioned in the screenshot
The concern is that if the normal user logged out then automatically admin user's token will be destroyed as both's user_id is 1 in oauth_clients table while guards is different for both
Please help me out for the same
It's also a security issue as your user with same id as admin can pass admin authentication middleware.
Following method is the cleanest workaround I've found.
You have to use different clients for your guards.
You have to run
passport:install
two times if you have two guards using Passport. it will generate two clients.
In new versions of Passport(I think after release of Laravel 8) when you are creating a client it asks you for a provider(Defined in auth.providers config). Each provider needs one client. If you are using old versions you can manually assign providers in oauth_clients table.
Now when generating token you have to specify client id.
In older versions you could do this by changing a public property of one of Passport classes but now you should register ClientRepository again.
App::clearResolvedInstance(ClientRepository::class);
app()->singleton(ClientRepository::class, function () {
return new ClientRepository(User::CLIENT_ID, null); // You should give the client id in the first parameter
});
$token = $user->createToken('TOKEN-EXAMPLE');
$accessToken = $token->accessToken;
Now if you check your access tokens table you can see that client ids are different.
Everything is fine now.
You mentioned there might be an issue with revoking tokens but I believe even in your case(using same client and not specifying providers) if you revoke a user token with same id as admin, admin's token will still remain. Revoking user token:
$user = Auth::guard('user-api')->user();
$user->token()->revoke();
does the guard apear anywhere on the token? or do tokens only have scopes? and if so, why would you use guards when you can use scopes...
good day!
[Edit]
in my case, im using roles and permissions to write my scopes... roles and permissions are guard based and a permission or role with a admin guard cannot be assigned to a user unless we add the 'admin' key with (in my case) 'passport' drive and 'user' provider...

Share Laravel Session on Subdomains with different guards

I'm currently using spatie/permissions and a Subclass of User with constraints to permissions to Login to subdomains in my application.
I now want to be able to share the session between my main domain example.com and the domains some.example.com where some is dynamically loaded from database.
When my logged-in User in example.com accesses the abc.example.com domain and is able to log in there I want to use the current session.
I use different guards on subdomain and domain with the SubUser and User classes as providers.
I already use the database session driver and can see in the logs that the same session id is loaded from database.
As the application is loading the same session from database I'm wondering why my user is not already logged in.
Anyone ever tried this and got a solution for this?
So I managed to resolve this issue.
My setup is all subdomains got the user guard and the main domain has the admin guard.
I realised that the Auth::getName() included the guard name and as I logged in using different guards I ended up having two active logins in one session. But these logins had different names and where only valid with the right guard. This guard being different in main domain and subdomains resulted in not really sharing login-state over domain and subdomains.
I managed to resolve this by overriding the default laravel SessionGuard and adding my own driver like so:
In config/auth.php:
'guards' => [
'user' => [
'driver' => 'extended_session',
'provider' => 'users',
],
'admin' => [
'driver' => 'extended_session',
'provider' => 'admins',
],
]
In AppServiceProvider.php
\Auth::extend('extended_session', function ($app, $name, $config) {
$providerConfig = $this->app['config']['auth.providers.'.$config['provider']];
// If you don't use eloquent you need to alter the next line accordingly
$provider = new EloquentUserProvider($app['hash'], $providerConfig['model']);
return new SessionGuardExtended('extended_session', $provider, $this->app['session.store']);
});
And add a new Class named SessionGuardExtended like this:
use Illuminate\Auth\SessionGuard;
class SessionGuardExtended extends SessionGuard{}
This results in a shared session with the same auth name for domain and subdomains.
Add SESSION_DOMAIN to your .env file and set it to .example.com

Laravel 5.3 using multiple authentication (e.g admin, customer)

I have read a lot of threads about multi-auth in Laravel, most of the configurations I see are somehow complicated, I have also seen a multi-auth package but it does not support Laravel Socialite.
I am aware that this question is asked multiple times, but if someone can give a better answer. It would be much appreciated!
Things I have tried
I am familiar with Laravel make:auth
I am also familiar with Laravel socialite Facebook , Twitter , Google plus.
Give this a try. But you still need some basic knowledge about Laravel's new multitenancy.
In config/auth.php add something like this to guards array:
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
Than in the same file add this to providers array:
'customers' => [
'driver' => 'eloquent',
'model' => App\Customer::class,
],
Than create Migration for customers DB table (you can use Laravel's out of the box migration for users table)
Next is Eloquent model App\Customer with these included:
use App\Scopes\AuthorizedScope;
use Illuminate\Foundation\Auth\User as Authenticatable;
These should let you use Laravel's Auth facade in your app with these most used methods:
Auth::guard('customer')->attempt()
Auth::guard('customer')->check()
Auth::guard('customer')->logout()
Auth::guard('customer')->user()
Or use auth middleware like this:
Route::get('customer/dashboard', function () {
// Only authenticated users may enter...
})->middleware('auth:customer');
Also checkout these:
https://laravel.com/docs/5.3/authentication#authenticating-users

How to fix in laravel 5.2 zizaco entrust:migration class name validation?

I have followed zizac/entrust installation tutorial from GitHub Link and faced with error:
Class name must be a valid object or a string in
var/www/html/laravel_test/vendor/zizaco/entrust/src/commands/MigrationCommand.php
on line 86
MigrationCommand.php file url : Link
Outut:
php artisan entrust:migration
Tables: roles, role_user, permissions, permission_role
A migration that creates 'roles', 'role_user', 'permissions', 'permission_role' tables will be created in database/migrations directory
Proceed with the migration creation? [Yes|no] (yes/no) [yes]: yes
Creating migration...
PHP Fatal error: Class name must be a valid object or a string in /var/www/html/laravel_test/vendor/zizaco/entrust/src/commands/MigrationCommand.php on line 86
the command: php artisan vendor:publish was successful.
File : config/entrust.php exist.
I didin't change any options to config/auth.php file same as - auth.php. How to fix it?
in vendor/zizaco/entrust/src/commands/MigrationCommand.php on line 86
remove line :
$usersTable = Config::get('auth.table');
$userModel = Config::get('auth.model');
add line :
$usersTable = Config::get('auth.providers.users.table');
$userModel = Config::get('auth.providers.users.model');
and config/auth.php file write provider line as like me :
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
'table' => 'users',
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
then your problem will solve : happy coding
In vendor/zizaco/entrust/src/commands/MigrationCommand.php on line 86.
Laravel 5.1.* Add Line
$usersTable = Config::get('auth.table');
$userModel = Config::get('auth.model');
Laravel 5.2.* Add Line
$usersTable = Config::get('auth.providers.users.table');
$userModel = Config::get('auth.providers.users.model');
The accepted answer may fix the problem but it is very bad practice to edit direct vendor files. The following will fix the issue you may be having and will support your app still working if you decide to update Entrust and they fix their codebase.
Add the following lines to config/auth.php underneath:
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
Laravel 5.1 - 5.4
'model' => \App\Models\User::class,
'table' => 'users',
Once Entrust rolls out an update you can remove this or keep it. Up to you.
Try running:
php artisan config:cache
to make sure your application is using fresh config files
EDIT
Ok, now I see, this library want to use:
$usersTable = Config::get('auth.table');
$userModel = Config::get('auth.model');
but there is no something like this in auth any more.
So as temporary workaround you should probaby add table and model to auth file like so: https://github.com/laravel/laravel/blob/5.1/config/auth.php
and wait until Entrust will be upgraded to remove this

Missing argument 1 for Illuminate\Auth\AuthManager::createDriver() lumen and JWT

I am trying to implement JWT token in my API using Lumen + JWT. I am using this JWT Library, I have set up it, but when I want to validate passed using JWTAuth::attempt($credentials) I get next error
ErrorException in AuthManager.php line 16:
Missing argument 1 for Illuminate\Auth\AuthManager::createDriver(), called in /home/admin/web/mkopilka.ru/public_html/api/referral/vendor/illuminate/support/Manager.php on line 87 and defined
I know where is the problem, but cannot figure out how to solve it because I don't know internals of framework well.
I have question about how does JWT authenticate the user (checks credentials in database, as I can gues it uses model class provided in jwt.php with the following line 'user' => 'App\Models\User'
By default 'user' => 'App\User'
So even if I changed user model in this file I got the next error
vendor/illuminate/auth/EloquentUserProvider.php line 126:
Class '\App\User' not found
I thought and decided to add config/auth.php file with succeeding content
return [
'model' => 'App\Models\User'
];
And now I get the the first exception.
What is wrong I can quess that I have overridden all parameters in auth config file.
Aslo I wonder where can I find (except source code, it will take a lot of time to understand it) explanation how JWTAuth::attempt works ?
Thanks.
Just had same issue myself and stumbled upon this question.
Solution is to add 'driver' => 'eloquent' into your created auth.php file.
I had the same problem on my upgrade from Laravel 4.1 to 4.2 (I think mainly because I updated all the files and tried to make a composer update afterwards).
For me the following worked (like reverting the relevant update steps):
1. Modify auth.php
Add driver, model and table to config/auth.php main array (additionally to the already existing one in the providers sub array):
<?php
return [
'driver' => 'eloquent',
'model' => App\User::class,
'table' => 'users',
// ...
2. Add ArtisanServiceProvider
To prevent the error Artisan: Command clear-compiled is not defined readd Illuminate\Foundation\Providers\ArtisanServiceProvider to the service providers
<?php
return [
// ...
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
// ...
3. Update and revert changes
Perform update (composer update) and revert the two previous steps by removing the added lines.

Categories