Как разграничить права токенов Laravel Sanctum

Рейтинг: 0Ответов: 2Опубликовано: 12.07.2023

В моем приложении Laravel 10 я использую пакет Spatie для ролей и Sanctum для авторизации.

Во время авторизации я создаю токен доступа с помощью Sanctum, передавая параметру abilities массив ролей, созданных Spatie во время регистрации:

$user->createToken($user->name, [
    $user->getRoleNames()->toArray()
])->plainTextToken;

По умолчанию у пользователей могут быть только две роли: user или admin.

При попытке обновить информацию другого пользователя (не того, которому был выдан токен доступа) Я получаю исключение AuthenticationException даже на уровне маршрута, прежде чем добраться до контроллеров.

Мои роуты выглядят так:

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('users', UserController::class);
});

Где и как можно (нужно) правильно настроить разрешения, чтобы пользователи с ролью user имели доступ только к получению информации и редактированию самих себя, а пользователи с ролью admin также имели доступ и к редактированию всех пользователей?

Ответы

▲ 1Принят

Покажу пример, как это делается с помощью политик.

Если учитывать, что админ может все. То сразу определяем это в провайдере:

class AuthServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Gate::before(function ($user, $ability) {
            return $user->hasRole('admin') ? true : null;
        });
    }
}

Можно использовать любые ваши проверки. В данном случае проверяется есть ли у пользователя роль admin. Подробнее про Gate::before.

Далее создаем саму политику. Покажу общий пример, все остальное делается по аналогии.

class UserPolicy
{
    public function update(User $user, User $object): bool
    {
        return $user->id === $object->id;
    }
}

$user - это текущий авторизованный пользователь. Падает сюда автоматически. $object - объект, над которым мы совершаем действие. Админа мы тут никак не проверяем, т.к. до этого мы разрешили ему делать все. Подробнее про политики. Если у вас не включено автоматическое обнаружение политик, вам нужно зарегистрировать их вручную. По умолчанию включено.

Далее нужно авторизовать действие. Например, можно так:

class UserController extends Controller
{
    public function update(User $user)
    {
       $this->authorize('update', $user);
       
       //Ваша логика
    }
}

Тут мы вызываем метод update нашей политики, передавая туда наш объект. Другие варианты авторизаций описаны здесь

Политика очень удобная вещь для вынесения логики авторизации.

▲ 0

Нашел решение.

Исправил присвоение полю abilities, у меня был вложенный массив, вместо обычного:

$user->createToken(
    $user->name,
    $user->getRoleNames()->toArray() // здесь будет ["user"] или ["admin"]
)->plainTextToken;

В метод обновления добавил проверку на наличие роли администратора, если идентификатор обновляемого пользователя отличается от пользователя, который авторизован:

if (!$updateRequest->user()->id != $id &&
    !$updateRequest->user()->tokenCan('admin')
) {
    throw new AuthenticationException();
}