Laravel Passport для PHP: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Laravel Passport для PHP: полное руководство по OAuth2 серверу

1. Что это и зачем нужна

Laravel Passport — это полноценная реализация OAuth2-сервера для фреймворка Laravel (PHP). Она предоставляет готовый набор инструментов для создания API-аутентификации, позволяя разработчикам быстро внедрить защиту своих RESTful API с использованием стандартных токенов доступа (access tokens) и обновления (refresh tokens). Библиотека построена на базе мощного пакета League OAuth2 Server, который адаптирован под экосистему Laravel, включая миграции, фасады, Artisan-команды и Eloquent-модели.

Основная цель Passport — избавить разработчика от необходимости писать сложную логику генерации, проверки и хранения токенов вручную. Вместо этого вы получаете готовые маршруты для выдачи токенов (через grant types), middleware для защиты роутов и удобные методы для управления клиентами (приложениями) и токенами. Passport идеально подходит для создания Single Page Applications (SPA), мобильных приложений и серверных клиентов, которым требуется безопасный доступ к вашему API.

Зачем использовать Passport, а не самописное решение? Во-первых, это безопасность: библиотека реализует все требования спецификации OAuth2, включая защиту от CSRF, корректную обработку scope и PKCE (Proof Key for Code Exchange). Во-вторых, это скорость разработки: вы получаете готовые миграции, модели и контроллеры. В-третьих, это гибкость: Passport поддерживает все основные grant types (authorization code, client credentials, password grant, personal access tokens) и легко расширяется под специфические нужды.

2. Установка

Установка Laravel Passport производится через Composer — стандартный менеджер пакетов для PHP. Выполните следующую команду в корне вашего Laravel-проекта:

composer require laravel/passport

После установки пакета необходимо выполнить миграции и опубликовать конфигурацию. Passport использует свою собственную систему миграций, которые создают таблицы для хранения клиентов, токенов, кодов авторизации и персональных токенов:

php artisan migrate

Затем запустите команду passport:install, которая создаст ключи шифрования и клиентов для персональных токенов и password grant:

php artisan passport:install

После выполнения команды вы увидите в консоли два client_id и client_secret. Первый — для персональных токенов (Personal Access Client), второй — для password grant (Password Grant Client). Сохраните их в файл .env:

PASSPORT_PERSONAL_ACCESS_CLIENT_ID=1
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=your-secret-here
PASSPORT_PASSWORD_GRANT_CLIENT_ID=2
PASSPORT_PASSWORD_GRANT_CLIENT_SECRET=your-secret-here

Далее добавьте трейт Laravel\Passport\HasApiTokens в модель User (app/Models/User.php):

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];
}

В файле config/auth.php измените драйвер аутентификации на passport:

'guards' => [
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Наконец, в файле app/Providers/AuthServiceProvider.php добавьте вызов Passport::routes() в метод boot():

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [];

    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
    }
}

3. Быстрый старт — минимальный рабочий пример

После установки и настройки создадим простой API-маршрут, защищенный Passport. Откройте файл routes/api.php и добавьте:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

Теперь, чтобы получить токен доступа, отправьте POST-запрос на /oauth/token (этот маршрут автоматически регистрируется Passport). Для password grant используйте следующие параметры:

// Пример запроса через cURL или любой HTTP-клиент
$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.test/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => env('PASSPORT_PASSWORD_GRANT_CLIENT_ID'),
        'client_secret' => env('PASSPORT_PASSWORD_GRANT_CLIENT_SECRET'),
        'username' => 'user@example.com',
        'password' => 'user-password',
        'scope' => '',
    ],
]);

$tokenData = json_decode((string) $response->getBody(), true);
// $tokenData['access_token'] — ваш токен доступа
// $tokenData['refresh_token'] — токен для обновления

Теперь используйте полученный access_token для доступа к защищенному маршруту:

$response = $http->get('http://your-app.test/api/user', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $tokenData['access_token'],
    ],
]);

$user = json_decode((string) $response->getBody(), true);
echo $user['email']; // user@example.com

4. Основные методы/функции/классы

4.1. Passport::routes()

Сигнатура: static void routes(null|callable $callback = null, array $options = [])

Что делает: Регистрирует все маршруты, необходимые для работы Passport: выдача токенов, управление клиентами, персональные токены и т.д.

// В AuthServiceProvider::boot()
Passport::routes(function ($router) {
    $router->forAuthorization();  // Только маршруты авторизации
    $router->forAccessTokens();   // Только маршруты для токенов
    $router->forTransientTokens(); // Только для временных токенов
    $router->forClients();        // Только для управления клиентами
    $router->forPersonalAccessTokens(); // Только для персональных токенов
});

4.2. Passport::tokensExpireIn()

Сигнатура: static void tokensExpireIn(DateTimeInterface $date)

Что делает: Устанавливает время жизни access-токенов. По умолчанию — 1 год.

// В AuthServiceProvider::boot()
use Carbon\Carbon;

Passport::tokensExpireIn(Carbon::now()->addDays(7)); // Токены живут 7 дней

4.3. Passport::refreshTokensExpireIn()

Сигнатура: static void refreshTokensExpireIn(DateTimeInterface $date)

Что делает: Устанавливает время жизни refresh-токенов. По умолчанию — 1 год.

Passport::refreshTokensExpireIn(Carbon::now()->addDays(30)); // Refresh токены живут 30 дней

4.4. Passport::personalAccessTokensExpireIn()

Сигнатура: static void personalAccessTokensExpireIn(DateTimeInterface $date)

Что делает: Устанавливает время жизни персональных access-токенов (Personal Access Tokens).

Passport::personalAccessTokensExpireIn(Carbon::now()->addMonths(6));

4.5. Passport::cookie()

Сигнатура: static void cookie(string $cookieName = 'laravel_token')

Что делает: Включает режим хранения токена в cookie (для SPA-приложений). Токен автоматически добавляется в ответ и считывается из запроса.

// В AuthServiceProvider::boot()
Passport::cookie(); // Использует имя 'laravel_token' по умолчанию

4.6. Passport::enableImplicitGrant()

Сигнатура: static void enableImplicitGrant()

Что делает: Включает поддержку Implicit Grant (устаревший, но иногда используемый тип авторизации).

Passport::enableImplicitGrant();

4.7. Passport::tokensCan()

Сигнатура: static void tokensCan(array $scopes)

Что делает: Определяет список доступных scope (разрешений) для токенов. Scope — это строковые идентификаторы прав доступа.

Passport::tokensCan([
    'read-posts' => 'Read posts',
    'write-posts' => 'Create and edit posts',
    'delete-posts' => 'Delete posts',
    'admin' => 'Full admin access',
]);

4.8. Passport::setDefaultScope()

Сигнатура: static void setDefaultScope(array $scopes)

Что делает: Устанавливает scope по умолчанию, если клиент не указал их в запросе.

Passport::setDefaultScope(['read-posts']);

4.9. Passport::scopeIds()

Сигнатура: static array scopeIds()

Что делает: Возвращает массив всех зарегистрированных scope ID (ключей).

$allScopeIds = Passport::scopeIds();
// ['read-posts', 'write-posts', 'delete-posts', 'admin']

4.10. Passport::scopesFor()

Сигнатура: static array scopesFor(array $ids)

Что делает: Возвращает массив объектов Scope для переданных ID.

$scopes = Passport::scopesFor(['read-posts', 'write-posts']);
foreach ($scopes as $scope) {
    echo $scope->id . ': ' . $scope->description;
}

4.11. Passport::hasScope()

Сигнатура: static bool hasScope(string $scope)

Что делает: Проверяет, зарегистрирован ли данный scope в системе.

if (Passport::hasScope('admin')) {
    echo 'Admin scope exists';
}

4.12. Passport::actingAs()

Сигнатура: static TestResponse actingAs(User $user, array $scopes = [])

Что делает: Вспомогательный метод для тестирования. Создает токен для указанного пользователя с заданными scope.

// В PHPUnit тесте
$user = User::factory()->create();

Passport::actingAs($user, ['read-posts']);

$response = $this->getJson('/api/posts');
$response->assertStatus(200);

5. Реальный практический пример

Создадим полноценное API для блога с аутентификацией через Passport. Будем использовать Authorization Code Grant с PKCE для SPA-приложения.

Шаг 1: Настройка модели User и миграции

<?php
// app/Models/User.php
namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    protected $fillable = ['name', 'email', 'password'];
    protected $hidden = ['password', 'remember_token'];

    // Связь с постами
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

Шаг 2: Создание модели Post и миграции

<?php
// app/Models/Post.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'content', 'user_id'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
<?php
// database/migrations/xxxx_create_posts_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema                    

Рекомендаций