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