spatie / laravel-permission

Associate users with roles and permissions
https://spatie.be/docs/laravel-permission
MIT License
12.21k stars 1.78k forks source link

Error "Class name must be a valid object or a string" caused by auth:sanctum middleware #1540

Closed JanickOtten closed 4 years ago

JanickOtten commented 4 years ago

After installing Laravel Sanctum and using the 'auth:sanctum' middleware instead of the 'auth:api' middleware, see Laravel docs, I cannot eager load users on the Role model. It returns "Class name must be a valid object or a string".

Before (working, without Sanctum installed) Middleware check in Controllers constructor. The index function returns all roles with eager loaded users and permissions.

class Controller
{
    public function __construct()
    {
        $this->middleware('auth:api');
    }

    public function index()
    {
        // Get all roles as array with eager loaded users and permissions
        $roles = Role::with('users', 'permissions')->get()->toArray();

        // Return response
        return response()->json($roles, 200);
    }
}

Now (using Laravel Sanctum, throws error) Same middleware check in constructor, but instead of 'auth:api', it checks for 'auth:sanctum', as per the Laravel docs. The index function should return the same response as before, however it throws "Class name must be a valid object or a string".

class Controller
{
    public function __construct()
    {
        $this->middleware('auth:sanctum');
    }

    public function index()
    {
        // Get all roles as array with eager loaded users and permissions
        $roles = Role::with('users', 'permissions')->get()->toArray();

        // Return response
        return response()->json($roles, 200);
    }
}

If I remove the 'users' eager load, it does eager load the permissions and return a response. I reckon it has to do with the 'sanctum' guard. I tried to initialize the Role model with the 'guard_name' attribute, but it returns the same error:

// Get all roles
$role = new Role(['guard_name' => 'web']); // 'guard_name' => 'sanctum' doesn't work either
$roles = $role->with('users', 'permissions')->get()->toArray();

Any ideas on how to eager load the users using Sanctum?

JanickOtten commented 4 years ago

It seems that Laravel Sanctums SanctumServiceProvider by default registers the sanctum guard with 'provider' => null:

class SanctumServiceProvider
{
    public function register()
    {
        config([
            'auth.guards.sanctum' => array_merge([
                'driver' => 'sanctum',
                'provider' => null,
            ], config('auth.guards.sanctum', [])),
        ]);

        ...
    }
}

This causes Laravel-Permissions getModelForGuard() helper function to return nothing when middleware requires the sanctum guard to be authenticated, hence the eager load fails. After manually inserting the guard in auth/config.php, the users could be eager loaded again. Not sure if it's the best solution though:

'guards' => [
    ...

    'sanctum' => [
        'driver' => 'sanctum',
        'provider' => 'users'
    ]
],
christhofer commented 4 years ago

@JanickOtten thanks, it solves mine too.

mra-dev commented 1 year ago

It seems that Laravel Sanctums SanctumServiceProvider by default registers the sanctum guard with 'provider' => null:

class SanctumServiceProvider
{
    public function register()
    {
        config([
            'auth.guards.sanctum' => array_merge([
                'driver' => 'sanctum',
                'provider' => null,
            ], config('auth.guards.sanctum', [])),
        ]);

        ...
    }
}

This causes Laravel-Permissions getModelForGuard() helper function to return nothing when middleware requires the sanctum guard to be authenticated, hence the eager load fails. After manually inserting the guard in auth/config.php, the users could be eager loaded again. Not sure if it's the best solution though:

'guards' => [
    ...

    'sanctum' => [
        'driver' => 'sanctum',
        'provider' => 'users'
    ]
],

Hi, I also have this problem. using Sanctum in SPA and getting user is now fine with this new config; But now spatie/permission return 403 for every gate and response.

Don't know how to fix this

parallels999 commented 1 year ago

@mra-dev maybe you are forgetting the guard_name in db

mra-dev commented 1 year ago

@parallels999 All of em set to "web"

parallels999 commented 1 year ago

@mra-dev exactly, there is your problem

mra-dev commented 1 year ago

@parallels999 What should i do then ?!

parallels999 commented 1 year ago

@mra-dev change it, dah 😄

mra-dev commented 1 year ago

Actually changed it from web to sanctum in PHPMyAdmin and cleared laravel cache & permission cache. And also config/auth.php default to sanctum Didn't work ? Did i miss somthing ?! @parallels999

mra-dev commented 1 year ago

@parallels999 Yes, i also ran "artisan permission:cache-reset"

mra-dev commented 1 year ago

Alright

new Error raised :

Method Illuminate\Auth\RequestGuard::login does not exist.

parallels999 commented 1 year ago

No idea then, make a failing test or a fresh instalation demostrating the problem Creating a laravel permission demo app

mra-dev commented 1 year ago

I think something's wrong with Sanctum.

When i set guards to: 'sanctum' => [ 'driver' => 'sanctum', 'provider' => 'users', ], i can get users, but spatie returns 403 every single request

but without the configuration above, No users but spatie works as expected.

mra-dev commented 1 year ago

Maybe i set that config on runtime for that specific request. don't like it, but i don't have enough time for it.