Laravel Passport
- Вступ
- Встановлення
- Конфігурація
- Надання коду авторизації
- Надання коду авторизації з PKCE
- Надання дозволу на пристрій
- Парольний грант
- Неявний дозвіл
- Клієнтські облікові дані Grant
- Персональні токени доступу
- Захист маршрутів
- Області дії токенів
- Аутентифікація SPA
- Події
- Тестування
Вступ
Laravel Passport надає повну реалізацію сервера OAuth2 для вашого Laravel застосунку за лічені хвилини. Passport побудований на основі League OAuth2 server, який підтримується Енді Міллінгтоном та Саймоном Гемпом.
Ця документація передбачає, що ви вже знайомі з OAuth2. Якщо ви нічого не знаєте про OAuth2, розгляньте можливість ознайомлення із загальною термінологією та функціями OAuth2 перед тим, як продовжити.
Passport чи Sanctum?
Перш ніж почати, можливо, ви захочете визначити, чи вашому застосунку краще підійде Laravel Passport або Laravel Sanctum. Якщо ваш застосунок обов'язково має підтримувати OAuth2, тоді вам слід використовувати Laravel Passport.
Однак, якщо ви намагаєтеся аутентифікувати односторінковий застосунок, мобільний застосунок або видавати API токени, вам слід використовувати Laravel Sanctum. Laravel Sanctum не підтримує OAuth2; однак, він забезпечує набагато простіший досвід розробки аутентифікації API.
Встановлення
Ви можете встановити Laravel Passport за допомогою команди Artisan install:api:
php artisan install:api --passport
Ця команда опублікує та виконає міграції бази даних, необхідні для створення таблиць, які вашому застосунку потрібні для зберігання OAuth2 клієнтів та токенів доступу. Команда також створить ключі шифрування, необхідні для генерації безпечних токенів доступу.
Після виконання команди install:api додайте трейти Laravel\Passport\HasApiTokens та інтерфейс Laravel\Passport\Contracts\OAuthenticatable до вашої моделі App\Models\User. Цей трейт надасть кілька допоміжних методів вашій моделі, які дозволять вам перевіряти токен та області дії автентифікованого користувача:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\Contracts\OAuthenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable implements OAuthenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
Нарешті, у файлі конфігурації config/auth.php вашого застосунку, ви повинні визначити охорону автентифікації api і встановити опцію driver на passport. Це вкаже вашому застосунку використовувати TokenGuard від Passport для автентифікації вхідних API-запитів:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Розгортання Passport
Коли ви вперше розгортаєте Passport на серверах вашого застосунку, вам, ймовірно, потрібно буде виконати команду passport:keys. Ця команда генерує ключі шифрування, які Passport потребує для створення токенів доступу. Зазвичай згенеровані ключі не зберігаються в системі контролю версій:
php artisan passport:keys
Якщо необхідно, ви можете визначити шлях, звідки повинні завантажуватися ключі Passport. Ви можете використовувати метод Passport::loadKeysFrom для цього. Зазвичай цей метод слід викликати з методу boot класу App\Providers\AppServiceProvider вашого застосунку:
/** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); }
Завантаження ключів з оточення
Альтернативно, ви можете опублікувати файл конфігурації Passport, використовуючи команду Artisan vendor:publish:
php artisan vendor:publish --tag=passport-config
Після публікації файлу конфігурації ви можете завантажити ключі шифрування вашого застосунку, визначивши їх як змінні середовища:
PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"
PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"
Оновлення Passport
Коли ви оновлюєтеся до нової основної версії Passport, важливо ретельно переглянути посібник з оновлення.
Конфігурація
Термін дії токенів
За замовчуванням, Passport видає довготривалі токени доступу, які закінчуються через один рік. Якщо ви хочете налаштувати довший або коротший термін дії токена, ви можете використовувати методи tokensExpireIn, refreshTokensExpireIn та personalAccessTokensExpireIn. Ці методи слід викликати з методу boot класу App\Providers\AppServiceProvider вашого застосунку:
use Carbon\CarbonInterval; /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::tokensExpireIn(CarbonInterval::days(15)); Passport::refreshTokensExpireIn(CarbonInterval::days(30)); Passport::personalAccessTokensExpireIn(CarbonInterval::months(6)); }
Стовпці expires_at у таблицях бази даних Passport є лише для читання і використовуються лише для відображення. Під час видачі токенів Passport зберігає інформацію про закінчення терміну дії в підписаних і зашифрованих токенах. Якщо вам потрібно анулювати токен, ви повинні анулювати його.
Перевизначення Моделей за Замовчуванням
Ви можете розширити моделі, які використовуються внутрішньо Passport, визначивши власну модель і розширивши відповідну модель Passport:
use Laravel\Passport\Client as PassportClient;
class Client extends PassportClient
{
// ...
}
Після визначення вашої моделі, ви можете вказати Passport використовувати вашу власну модель через клас Laravel\Passport\Passport. Зазвичай, ви повинні повідомити Passport про ваші власні моделі в методі boot класу App\Providers\AppServiceProvider вашого застосунку:
use App\Models\Passport\AuthCode; use App\Models\Passport\Client; use App\Models\Passport\DeviceCode; use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; use Laravel\Passport\Passport; /** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::useTokenModel(Token::class); Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); Passport::useClientModel(Client::class); Passport::useDeviceCodeModel(DeviceCode::class); }
Перевизначення маршрутів
Іноді ви можете захотіти налаштувати маршрути, визначені Passport. Щоб досягти цього, спочатку потрібно ігнорувати маршрути, зареєстровані Passport, додавши Passport::ignoreRoutes до методу register вашого AppServiceProvider застосунку:
use Laravel\Passport\Passport; /** * Зареєструвати будь-які служби застосунку. */ public function register(): void { Passport::ignoreRoutes(); }
Потім ви можете скопіювати маршрути, визначені Passport, у його файлі маршрутів до файлу routes/web.php вашого застосунку та змінити їх на свій розсуд:
Route::group([ 'as' => 'passport.', 'prefix' => config('passport.path', 'oauth'), 'namespace' => '\Laravel\Passport\Http\Controllers', ], function () { // Маршрути Passport... });
Код авторизації Grant
Використання OAuth2 через коди авторизації - це те, як більшість розробників знайомі з OAuth2. При використанні кодів авторизації клієнтський застосунок перенаправить користувача на ваш сервер, де він або схвалить, або відхилить запит на видачу токена доступу клієнту.
Щоб розпочати, нам потрібно вказати Passport, як повертати наше представлення "authorization".
Весь рендеринг логіки представлення авторизації може бути налаштований за допомогою відповідних методів, доступних через клас Laravel\Passport\Passport. Зазвичай, ви повинні викликати цей метод з методу boot класу App\Providers\AppServiceProvider вашого застосунку:
use Inertia\Inertia;
use Laravel\Passport\Passport;
/**
* Завантажте будь-які сервіси застосунку.
*/
public function boot(): void
{
// Надавши ім'я представлення...
Passport::authorizationView('auth.oauth.authorize');
// Надаючи замикання...
Passport::authorizationView(
fn ($parameters) => Inertia::render('Auth/OAuth/Authorize', [
'request' => $parameters['request'],
'authToken' => $parameters['authToken'],
'client' => $parameters['client'],
'user' => $parameters['user'],
'scopes' => $parameters['scopes'],
])
);
}
Passport автоматично визначить маршрут /oauth/authorize, який повертає це представлення. Ваш шаблон auth.oauth.authorize повинен включати форму, яка робить POST-запит до маршруту passport.authorizations.approve для схвалення авторизації, та форму, яка робить DELETE-запит до маршруту passport.authorizations.deny для відхилення авторизації. Маршрути passport.authorizations.approve та passport.authorizations.deny очікують поля state, client_id та auth_token.
Управління клієнтами
Розробники, які створюють застосунки, що потребують взаємодії з API вашого застосунку, повинні зареєструвати свій застосунок у вашому, створивши "клієнта". Зазвичай це складається з надання назви їхнього застосунку та URI, на який ваш застосунок може перенаправити після того, як користувачі схвалять їхній запит на авторизацію.
Клієнти першої сторони
Найпростіший спосіб створити клієнта - це використання команди Artisan passport:client. Ця команда може бути використана для створення клієнтів першої сторони або тестування вашої функціональності OAuth2. Коли ви запускаєте команду passport:client, Passport запитає у вас додаткову інформацію про вашого клієнта і надасть вам ідентифікатор клієнта та секрет:
php artisan passport:client
Якщо ви хочете дозволити кілька URI перенаправлення для вашого клієнта, ви можете вказати їх, використовуючи список, розділений комами, коли вас запитають про URI командою passport:client. Будь-які URI, які містять коми, повинні бути закодовані в URI:
https://third-party-app.com/callback,https://example.com/oauth/redirect
Сторонні клієнти
Оскільки користувачі вашого застосунку не зможуть використовувати команду passport:client, ви можете скористатися методом createAuthorizationCodeGrantClient класу Laravel\Passport\ClientRepository для реєстрації клієнта для заданого користувача:
use App\Models\User;
use Laravel\Passport\ClientRepository;
$user = User::find($userId);
// Створення OAuth додатку-клієнта, що належить вказаному користувачу...
$client = app(ClientRepository::class)->createAuthorizationCodeGrantClient(
user: $user,
name: 'Example App',
redirectUris: ['https://third-party-app.com/callback'],
confidential: false,
enableDeviceFlow: true
);
// Отримання всіх OAuth клієнтів додатків, які належать користувачу...
$clients = $user->oauthApps()->get();
Метод createAuthorizationCodeGrantClient повертає екземпляр Laravel\Passport\Client. Ви можете відобразити $client->id як ID клієнта та $client->plainSecret як секрет клієнта для користувача.
Запит токенів
Перенаправлення для авторизації
Після створення клієнта розробники можуть використовувати їхній ідентифікатор клієнта та секрет для запиту коду авторизації та токена доступу від вашого застосунку. Спочатку застосунок, що споживає, повинен зробити запит на перенаправлення до маршруту вашого застосунку /oauth/authorize таким чином:
use Illuminate\Http\Request; use Illuminate\Support\Str; Route::get('/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ 'client_id' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });
Параметр prompt може бути використаний для визначення поведінки автентифікації застосунку Passport.
Якщо значення prompt є none, Passport завжди викине помилку автентифікації, якщо користувач ще не автентифікований у застосунку Passport. Якщо значення є consent, Passport завжди відображатиме екран підтвердження авторизації, навіть якщо всі області вже були надані споживаючому застосунку. Коли значення є login, застосунок Passport завжди вимагатиме від користувача повторно увійти в застосунок, навіть якщо у них вже є існуюча сесія.
Якщо значення prompt не надано, користувач буде запитаний про авторизацію лише в тому випадку, якщо він раніше не надавав доступ до споживаючого застосунку для запитуваних областей.
Пам'ятайте, маршрут /oauth/authorize вже визначений Passport. Вам не потрібно вручну визначати цей маршрут.
Затвердження запиту
Коли отримуються запити на авторизацію, Passport автоматично відповідатиме на основі значення параметра prompt (якщо він присутній) і може відобразити шаблон для користувача, дозволяючи йому схвалити або відхилити запит на авторизацію. Якщо вони схвалять запит, їх буде перенаправлено назад на redirect_uri, який був вказаний споживаючим застосунком. redirect_uri повинен відповідати URL-адресі redirect, яка була вказана при створенні клієнта.
Іноді ви можете захотіти пропустити запит на авторизацію, наприклад, коли авторизуєте клієнта першої сторони. Ви можете досягти цього, розширивши модель Client і визначивши метод skipsAuthorization. Якщо skipsAuthorization повертає true, клієнт буде схвалений, і користувач буде негайно перенаправлений назад до redirect_uri, якщо тільки споживаючий застосунок явно не встановив параметр prompt при перенаправленні для авторизації:
<?php
namespace App\Models\Passport;
use Illuminate\Contracts\Auth\Authenticatable;
use Laravel\Passport\Client as BaseClient;
class Client extends BaseClient
{
/**
* Визначте, чи повинен клієнт пропустити запит на авторизацію.
*
* @param \Laravel\Passport\Scope[] $scopes
*/
public function skipsAuthorization(Authenticatable $user, array $scopes): bool
{
return $this->firstParty();
}
}
Перетворення кодів авторизації на токени доступу
Якщо користувач схвалює запит на авторизацію, його буде перенаправлено назад до споживаючого застосунку. Споживач повинен спочатку перевірити параметр state на відповідність значенню, яке було збережено перед перенаправленням. Якщо параметр state збігається, тоді споживач повинен надіслати POST запит до вашого застосунку, щоб запросити токен доступу. Запит повинен включати код авторизації, який був виданий вашим застосунком, коли користувач схвалив запит на авторизацію:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class,
'Invalid state value.'
);
$response = Http::asForm()->post('https://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'your-client-id',
'client_secret' => 'your-client-secret',
'redirect_uri' => 'https://third-party-app.com/callback',
'code' => $request->code,
]);
return $response->json();
});
Цей маршрут /oauth/token поверне JSON-відповідь, що містить атрибути access_token, refresh_token та expires_in. Атрибут expires_in містить кількість секунд до закінчення терміну дії токена доступу.
Як і маршрут /oauth/authorize, маршрут /oauth/token визначається для вас за допомогою Passport. Немає потреби вручну визначати цей маршрут.
Управління токенами
Ви можете отримати авторизовані токени користувача, використовуючи метод tokens трейтa Laravel\Passport\HasApiTokens. Наприклад, це може бути використано для надання вашим користувачам панелі керування для відстеження їхніх з'єднань із сторонніми застосунками:
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Date;
use Laravel\Passport\Token;
$user = User::find($userId);
// Отримання всіх дійсних токенів для користувача...
$tokens = $user->tokens()
->where('revoked', false)
->where('expires_at', '>', Date::now())
->get();
// Отримання всіх підключень користувача до сторонніх клієнтів OAuth додатків...
$connections = $tokens->load('client')
->reject(fn (Token $token) => $token->client->firstParty())
->groupBy('client_id')
->map(fn (Collection $tokens) => [
'client' => $tokens->first()->client,
'scopes' => $tokens->pluck('scopes')->flatten()->unique()->values()->all(),
'tokens_count' => $tokens->count(),
])
->values();
Оновлення токенів
Якщо ваш застосунок видає короткострокові токени доступу, користувачам потрібно буде оновити свої токени доступу за допомогою токена оновлення, який був наданий їм, коли токен доступу був виданий:
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // Потрібно лише для конфіденційних клієнтів... 'scope' => 'user:read orders:create', ]); return $response->json();
Цей маршрут /oauth/token поверне JSON-відповідь, що містить атрибути access_token, refresh_token та expires_in. Атрибут expires_in містить кількість секунд до закінчення терміну дії токена доступу.
Відкликання токенів
Ви можете відкликати токен, використовуючи метод revoke на моделі Laravel\Passport\Token. Ви можете відкликати refresh токен, використовуючи метод revoke на моделі Laravel\Passport\RefreshToken:
use Laravel\Passport\Passport;
use Laravel\Passport\Token;
$token = Passport::token()->find($tokenId);
// Відкликати токен доступу...
$token->revoke();
// Відкликати токен оновлення токена...
$token->refreshToken?->revoke();
// Відкликати всі токени користувача...
User::find($userId)->tokens()->each(function (Token $token) {
$token->revoke();
$token->refreshToken?->revoke();
});
Очищення токенів
Коли токени були відкликані або термін їх дії закінчився, ви можете захотіти видалити їх з бази даних. Включена команда Artisan passport:purge у Passport може зробити це за вас:
# Очистити відкликані та прострочені токени, коди авторизації та коди пристроїв... php artisan passport:purge # Очистити лише токени, що прострочені понад 6 годин... php artisan passport:purge --hours=6 # Очистити лише відкликані токени, коди авторизації та коди пристроїв... php artisan passport:purge --revoked # Очистити лише прострочені токени, коди авторизації та коди пристроїв... php artisan passport:purge --expired
Ви також можете налаштувати заплановане завдання у файлі routes/console.php вашого застосунку, щоб автоматично очищати ваші токени за розкладом:
use Illuminate\Support\Facades\Schedule;
Schedule::command('passport:purge')->hourly();
Код авторизації з PKCE
Грант "Код авторизації" з "Доказом ключа для обміну кодом" (PKCE) є безпечним способом аутентифікації односторінкових застосунків або мобільних застосунків для доступу до вашого API. Цей грант слід використовувати, коли ви не можете гарантувати, що секрет клієнта буде зберігатися конфіденційно, або для зменшення загрози перехоплення коду авторизації зловмисником. Поєднання "перевірника коду" та "виклику коду" замінює секрет клієнта при обміні коду авторизації на токен доступу.
Створення клієнта
Перш ніж ваш застосунок зможе видавати токени через грант коду авторизації з PKCE, вам потрібно створити клієнта з підтримкою PKCE. Ви можете зробити це за допомогою Artisan команди passport:client з опцією --public:
php artisan passport:client --public
Запит токенів
Перевірка коду та Виклик коду
Оскільки цей дозвільний грант не надає секрет клієнта, розробникам потрібно буде згенерувати комбінацію перевіряючого коду та виклику коду, щоб запросити токен.
Код перевірки повинен бути випадковим рядком від 43 до 128 символів, що містить літери, цифри та символи "-", ".", "_", "~", як визначено в специфікації RFC 7636.
Кодове завдання має бути рядком, закодованим у Base64, з безпечними для URL та імен файлів символами. Завершальні символи '=' мають бути видалені, і не повинно бути жодних розривів рядків, пробілів або інших додаткових символів.
$encoded = base64_encode(hash('sha256', $codeVerifier, true));
$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
Перенаправлення для авторизації
Після створення клієнта ви можете використовувати ідентифікатор клієнта та згенеровані код перевірки та код виклику, щоб запросити код авторизації та токен доступу від вашого застосунку. Спочатку застосунок, що споживає, повинен зробити запит на перенаправлення до маршруту /oauth/authorize вашого застосунку:
use Illuminate\Http\Request; use Illuminate\Support\Str; Route::get('/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $request->session()->put( 'code_verifier', $codeVerifier = Str::random(128) ); $codeChallenge = strtr(rtrim( base64_encode(hash('sha256', $codeVerifier, true)) , '='), '+/', '-_'); $query = http_build_query([ 'client_id' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', 'state' => $state, 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });
Перетворення кодів авторизації на токени доступу
Якщо користувач схвалює запит на авторизацію, його буде перенаправлено назад до споживаючого застосунку. Споживач повинен перевірити параметр state на відповідність значенню, яке було збережено перед перенаправленням, як у стандартному Authorization Code Grant.
Якщо параметр state збігається, споживач повинен надіслати POST запит до вашого застосунку, щоб запросити токен доступу. Запит повинен включати код авторизації, який був виданий вашим застосунком, коли користувач схвалив запит на авторизацію, разом з початково згенерованим перевірником коду:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
$codeVerifier = $request->session()->pull('code_verifier');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class
);
$response = Http::asForm()->post('https://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'your-client-id',
'redirect_uri' => 'https://third-party-app.com/callback',
'code_verifier' => $codeVerifier,
'code' => $request->code,
]);
return $response->json();
});
Надання авторизації пристрою
OAuth2 дозвіл на пристрій дозволяє пристроям без браузера або з обмеженим введенням, таким як телевізори та ігрові консолі, отримати токен доступу шляхом обміну "кодом пристрою". При використанні потоку пристрою, клієнт пристрою інструктує користувача використовувати вторинний пристрій, такий як комп'ютер або смартфон, і підключитися до вашого сервера, де вони введуть наданий "код користувача" і або схвалять, або відхилять запит на доступ.
Щоб розпочати, нам потрібно вказати Passport, як повертати наші представлення "user code" та "authorization".
Весь рендеринг логіки представлення авторизації може бути налаштований за допомогою відповідних методів, доступних через клас Laravel\Passport\Passport. Зазвичай, ви повинні викликати цей метод з методу boot класу App\Providers\AppServiceProvider вашого застосунку.
use Inertia\Inertia;
use Laravel\Passport\Passport;
/**
* Запустіть будь-які сервіси застосунку.
*/
public function boot(): void
{
// Надавши ім'я представлення...
Passport::deviceUserCodeView('auth.oauth.device.user-code');
Passport::deviceAuthorizationView('auth.oauth.device.authorize');
// Надаючи замикання...
Passport::deviceUserCodeView(
fn ($parameters) => Inertia::render('Auth/OAuth/Device/UserCode')
);
Passport::deviceAuthorizationView(
fn ($parameters) => Inertia::render('Auth/OAuth/Device/Authorize', [
'request' => $parameters['request'],
'authToken' => $parameters['authToken'],
'client' => $parameters['client'],
'user' => $parameters['user'],
'scopes' => $parameters['scopes'],
])
);
// ...
}
Passport автоматично визначить маршрути, які повертають ці представлення. Ваш шаблон auth.oauth.device.user-code повинен містити форму, яка робить GET-запит до маршруту passport.device.authorizations.authorize. Маршрут passport.device.authorizations.authorize очікує параметр запиту user_code.
Ваш шаблон auth.oauth.device.authorize повинен включати форму, яка робить POST-запит до маршруту passport.device.authorizations.approve для затвердження авторизації, та форму, яка робить DELETE-запит до маршруту passport.device.authorizations.deny для відхилення авторизації. Маршрути passport.device.authorizations.approve та passport.device.authorizations.deny очікують поля state, client_id та auth_token.
Створення клієнта для надання дозволу на пристрій
Перш ніж ваш застосунок зможе видавати токени через дозвіл на пристрій, вам потрібно створити клієнта з увімкненим потоком пристроїв. Ви можете зробити це за допомогою Artisan команди passport:client з опцією --device. Ця команда створить клієнта з увімкненим потоком пристроїв першої сторони та надасть вам ідентифікатор клієнта та секрет:
php artisan passport:client --device
Крім того, ви можете використовувати метод createDeviceAuthorizationGrantClient у класі ClientRepository, щоб зареєструвати стороннього клієнта, який належить вказаному користувачу:
use App\Models\User;
use Laravel\Passport\ClientRepository;
$user = User::find($userId);
$client = app(ClientRepository::class)->createDeviceAuthorizationGrantClient(
user: $user,
name: 'Example Device',
confidential: false,
);
Запит токенів
Запит коду пристрою
Як тільки клієнт створений, розробники можуть використовувати його ідентифікатор клієнта для запиту коду пристрою з вашого застосунку. Спочатку пристрій, що споживає, повинен зробити запит POST до маршруту вашого застосунку /oauth/device/code для запиту коду пристрою:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('https://passport-app.test/oauth/device/code', [
'client_id' => 'your-client-id',
'scope' => 'user:read orders:create',
]);
return $response->json();
Це поверне JSON-відповідь, що містить атрибути device_code, user_code, verification_uri, interval та expires_in. Атрибут expires_in містить кількість секунд до закінчення терміну дії коду пристрою. Атрибут interval містить кількість секунд, яку споживаючий пристрій повинен чекати між запитами при опитуванні маршруту /oauth/token, щоб уникнути помилок обмеження швидкості.
Пам'ятайте, маршрут /oauth/device/code вже визначений Passport. Вам не потрібно вручну визначати цей маршрут.
Відображення URI для верифікації та коду користувача
Після отримання запиту на код пристрою, пристрій, що споживає, повинен проінструктувати користувача використовувати інший пристрій і відвідати наданий verification_uri та ввести user_code для затвердження запиту на авторизацію.
Запит на отримання токена опитування
Оскільки користувач буде використовувати окремий пристрій для надання (або відмови) доступу, пристрій, що споживає, повинен опитувати маршрут вашого застосунку /oauth/token, щоб визначити, коли користувач відповів на запит. Пристрій, що споживає, повинен використовувати мінімальний інтервал опитування, наданий у JSON-відповіді при запиті коду пристрою, щоб уникнути помилок обмеження швидкості:
use Illuminate\Support\Facades\Http; use Illuminate\Support\Sleep; $interval = 5; do { Sleep::for($interval)->seconds(); $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // Потрібно лише для конфіденційних клієнтів... 'device_code' => 'the-device-code', ]); if ($response->json('error') === 'slow_down') { $interval += 5; } } while (in_array($response->json('error'), ['authorization_pending', 'slow_down'])); return $response->json();
Якщо користувач схвалив запит на авторизацію, це поверне JSON-відповідь, що містить атрибути access_token, refresh_token та expires_in. Атрибут expires_in містить кількість секунд до закінчення терміну дії токена доступу.
Парольний грант
Ми більше не рекомендуємо використовувати токени надання пароля. Натомість, вам слід обрати тип надання, який наразі рекомендується сервером OAuth2.
OAuth2 парольний грант дозволяє вашим іншим клієнтам першої сторони, таким як мобільний застосунок, отримувати токен доступу, використовуючи адресу електронної пошти / ім'я користувача та пароль. Це дозволяє вам безпечно видавати токени доступу вашим клієнтам першої сторони без необхідності проходження вашими користувачами всього процесу перенаправлення з кодом авторизації OAuth2.
Щоб увімкнути надання пароля, викличте метод enablePasswordGrant у методі boot класу App\Providers\AppServiceProvider вашого застосунку:
/** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::enablePasswordGrant(); }
Створення клієнта з паролем доступу
Перш ніж ваш застосунок зможе видавати токени через password grant, вам потрібно створити клієнта password grant. Ви можете зробити це за допомогою Artisan команди passport:client з опцією --password.
php artisan passport:client --password
Запит токенів
Після того, як ви активували грант і створили клієнта з паролем, ви можете запросити токен доступу, надіславши POST запит на маршрут /oauth/token з електронною адресою та паролем користувача. Пам'ятайте, цей маршрут вже зареєстрований Passport, тому немає потреби визначати його вручну. Якщо запит буде успішним, ви отримаєте access_token та refresh_token у JSON-відповіді від сервера:
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // Потрібно лише для конфіденційних клієнтів... 'username' => 'example@example.com', 'password' => 'my-password', 'scope' => 'user:read orders:create', ]); return $response->json();
Пам'ятайте, що токени доступу за замовчуванням мають тривалий термін дії. Однак, ви можете налаштувати максимальний термін дії токена доступу, якщо це необхідно.
Запит усіх областей
Коли ви використовуєте парольний грант або грант клієнтських облікових даних, ви можете захотіти авторизувати токен для всіх областей, підтримуваних вашим застосунком. Ви можете зробити це, запитуючи область *. Якщо ви запитуєте область *, метод can на екземплярі токена завжди повертатиме true. Ця область може бути призначена лише токену, який видано з використанням гранту password або client_credentials:
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // Потрібно лише для конфіденційних клієнтів... 'username' => 'example@example.com', 'password' => 'my-password', 'scope' => '*', ]);
Налаштування Провайдера Користувача
Якщо ваш застосунок використовує більше ніж одного постачальника користувачів для автентифікації, ви можете вказати, який постачальник користувачів використовує клієнт паролів, надавши опцію --provider при створенні клієнта через команду artisan passport:client --password. Вказане ім'я постачальника повинно відповідати дійсному постачальнику, визначеному у файлі конфігурації вашого застосунку config/auth.php. Потім ви можете захистити свій маршрут за допомогою middleware, щоб гарантувати, що тільки користувачі з вказаного постачальника guard мають авторизацію.
Налаштування поля імені користувача
Коли аутентифікація здійснюється за допомогою пароля, Passport використовуватиме атрибут email вашої моделі, що може бути аутентифікована, як "ім'я користувача". Однак, ви можете налаштувати цю поведінку, визначивши метод findForPassport у вашій моделі:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\Contracts\OAuthenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable implements OAuthenticatable
{
use HasApiTokens, Notifiable;
/**
* Знайдіть екземпляр користувача за вказаним ім'ям користувача.
*/
public function findForPassport(string $username): User
{
return $this->where('username', $username)->first();
}
}
Налаштування перевірки пароля
Коли аутентифікація здійснюється за допомогою password grant, Passport використовуватиме атрибут password вашої моделі для перевірки наданого пароля. Якщо ваша модель не має атрибута password або ви бажаєте налаштувати логіку перевірки пароля, ви можете визначити метод validateForPassportPasswordGrant у вашій моделі:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Passport\Contracts\OAuthenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable implements OAuthenticatable
{
use HasApiTokens, Notifiable;
/**
* Перевірте пароль користувача для надання пароля Passport.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}
Неявний дозвіл
Ми більше не рекомендуємо використовувати токени неявного надання. Натомість, вам слід обрати тип надання, який наразі рекомендується сервером OAuth2.
Імпліцитний грант схожий на грант коду авторизації; однак, токен повертається клієнту без обміну коду авторизації. Цей грант найчастіше використовується для JavaScript або мобільних застосунків, де облікові дані клієнта не можуть бути безпечно збережені. Щоб увімкнути грант, викличте метод enableImplicitGrant у методі boot класу App\Providers\AppServiceProvider вашого застосунку:
/** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::enableImplicitGrant(); }
Перш ніж ваш застосунок зможе видавати токени через implicit grant, вам потрібно створити клієнта для implicit grant. Ви можете зробити це за допомогою Artisan команди passport:client з опцією --implicit.
php artisan passport:client --implicit
Як тільки грант було активовано і неявний клієнт створено, розробники можуть використовувати свій ідентифікатор клієнта для запиту токена доступу з вашого застосунку. Застосунок, що споживає, повинен зробити запит на перенаправлення до маршруту /oauth/authorize вашого застосунку наступним чином:
use Illuminate\Http\Request; Route::get('/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ 'client_id' => 'your-client-id', 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'token', 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('https://passport-app.test/oauth/authorize?'.$query); });
Пам'ятайте, маршрут /oauth/authorize вже визначений Passport. Вам не потрібно вручну визначати цей маршрут.
Надання облікових даних клієнта
Грант облікових даних клієнта підходить для автентифікації машина-машина. Наприклад, ви можете використовувати цей грант у запланованій задачі, яка виконує завдання з обслуговування через API.
Перш ніж ваш застосунок зможе видавати токени через грант клієнтських облікових даних, вам потрібно створити клієнт для гранту клієнтських облікових даних. Ви можете зробити це за допомогою опції --client команди Artisan passport:client:
php artisan passport:client --client
Далі, призначте Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner middleware до маршруту:
use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner;
Route::get('/orders', function (Request $request) {
// Токен доступу є дійсним, і клієнт є власником ресурсу...
})->middleware(EnsureClientIsResourceOwner::class);
Щоб обмежити доступ до маршруту певними областями, ви можете надати список необхідних областей методу using:
Route::get('/orders', function (Request $request) {
// Токен доступу є дійсним, клієнт є власником ресурсу і має обидві області "servers:read" і "servers:create"...
})->middleware(EnsureClientIsResourceOwner::using('servers:read', 'servers:create');
Отримання токенів
Щоб отримати токен, використовуючи цей тип гранту, зробіть запит до кінцевої точки oauth/token:
use Illuminate\Support\Facades\Http;
$response = Http::asForm()->post('https://passport-app.test/oauth/token', [
'grant_type' => 'client_credentials',
'client_id' => 'your-client-id',
'client_secret' => 'your-client-secret',
'scope' => 'servers:read servers:create',
]);
return $response->json()['access_token'];
Особисті токени доступу
Іноді ваші користувачі можуть захотіти видати собі токени доступу без проходження типового потоку перенаправлення з кодом авторизації. Дозволити користувачам видавати токени собі через інтерфейс вашого застосунку може бути корисним для того, щоб дозволити користувачам експериментувати з вашим API або може слугувати простішим підходом до видачі токенів доступу загалом.
Якщо ваш застосунок використовує Passport в основному для видачі токенів особистого доступу, розгляньте можливість використання Laravel Sanctum, легковагової бібліотеки першої сторони Laravel для видачі токенів доступу до API.
Створення клієнта особистого доступу
Перш ніж ваш застосунок зможе видавати токени особистого доступу, вам потрібно створити клієнта особистого доступу. Ви можете зробити це, виконавши Artisan команду passport:client з опцією --personal. Якщо ви вже виконали команду passport:install, вам не потрібно виконувати цю команду:
php artisan passport:client --personal
Налаштування Провайдера Користувача
Якщо ваш застосунок використовує більше ніж одного постачальника користувачів для автентифікації, ви можете вказати, який постачальник користувачів використовує клієнт з персональним доступом, надавши опцію --provider при створенні клієнта через команду artisan passport:client --personal. Вказане ім'я постачальника повинно відповідати дійсному постачальнику, визначеному у файлі конфігурації вашого застосунку config/auth.php. Потім ви можете захистити свій маршрут за допомогою middleware, щоб гарантувати, що тільки користувачі з вказаного постачальника guard мають авторизацію.
Управління Персональними Токенами Доступу
Після того, як ви створили клієнта з персональним доступом, ви можете видавати токени для даного користувача, використовуючи метод createToken на екземплярі моделі App\Models\User. Метод createToken приймає назву токена як свій перший аргумент і необов'язковий масив областей дії як другий аргумент:
use App\Models\User;
use Illuminate\Support\Facades\Date;
use Laravel\Passport\Token;
$user = User::find($userId);
// Створення токена без областей...
$token = $user->createToken('My Token')->accessToken;
// Створення токена з областями...
$token = $user->createToken('My Token', ['user:read', 'orders:create'])->accessToken;
// Створення токена з усіма областями...
$token = $user->createToken('My Token', ['*'])->accessToken;
// Отримання всіх дійсних персональних токенів доступу, які належать користувачу...
$tokens = $user->tokens()
->with('client')
->where('revoked', false)
->where('expires_at', '>', Date::now())
->get()
->filter(fn (Token $token) => $token->client->hasGrantType('personal_access'));
Захист маршрутів
Через Middleware
Passport включає аутентифікаційний guard, який буде перевіряти токени доступу на вхідних запитах. Після того як ви налаштували guard api для використання драйвера passport, вам потрібно лише вказати middleware auth:api на будь-яких маршрутах, які повинні вимагати дійсний токен доступу:
Route::get('/user', function () {
// Тільки автентифіковані користувачі API можуть отримати доступ до цього маршруту...
})->middleware('auth:api');
Якщо ви використовуєте дозвіл на облікові дані клієнта, вам слід використовувати middleware Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner для захисту ваших маршрутів замість middleware auth:api.
Кілька Аутентифікаційних Охоронців
Якщо ваш застосунок автентифікує різні типи користувачів, які, можливо, використовують абсолютно різні моделі Eloquent, вам, ймовірно, потрібно буде визначити конфігурацію guard для кожного типу постачальника користувачів у вашому застосунку. Це дозволяє захищати запити, призначені для конкретних постачальників користувачів. Наприклад, враховуючи наступну конфігурацію guard у файлі конфігурації config/auth.php:
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'api-customers' => [
'driver' => 'passport',
'provider' => 'customers',
],
],
Наступний маршрут буде використовувати охорону api-customers, яка використовує постачальника користувачів customers, для автентифікації вхідних запитів:
Route::get('/customer', function () {
// ...
})->middleware('auth:api-customers');
Для отримання додаткової інформації про використання декількох постачальників користувачів з Passport, будь ласка, зверніться до документації з персональних токенів доступу та документації з паролів.
Передача токена доступу
Коли викликаються маршрути, які захищені за допомогою Passport, споживачі API вашого застосунку повинні вказати свій токен доступу як токен Bearer у заголовку Authorization свого запиту. Наприклад, при використанні фасаду Http:
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'Accept' => 'application/json',
'Authorization' => "Bearer $accessToken",
])->get('https://passport-app.test/api/user');
return $response->json();
Області дії токенів
Scopes дозволяють вашим клієнтам API запитувати певний набір дозволів при запиті авторизації для доступу до облікового запису. Наприклад, якщо ви створюєте e-commerce застосунок, не всім споживачам API буде потрібна можливість розміщувати замовлення. Замість цього ви можете дозволити споживачам лише запитувати авторизацію для доступу до статусів відправлення замовлень. Іншими словами, scopes дозволяють користувачам вашого застосунку обмежувати дії, які сторонній застосунок може виконувати від їхнього імені.
Визначення Областей
Ви можете визначити області вашого API, використовуючи метод Passport::tokensCan у методі boot класу App\Providers\AppServiceProvider вашого застосунку. Метод tokensCan приймає масив імен областей та описів областей. Опис області може бути будь-яким, як ви бажаєте, і буде відображатися користувачам на екрані підтвердження авторизації:
/** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::tokensCan([ 'user:read' => 'Retrieve the user info', 'orders:create' => 'Place orders', 'orders:read:status' => 'Check order status', ]); }
Типовий обсяг
Якщо клієнт не запитує жодних конкретних областей, ви можете налаштувати ваш сервер Passport для прикріплення областей за замовчуванням до токена, використовуючи метод defaultScopes. Зазвичай, ви повинні викликати цей метод з методу boot класу App\Providers\AppServiceProvider вашого застосунку:
use Laravel\Passport\Passport;
Passport::tokensCan([
'user:read' => 'Retrieve the user info',
'orders:create' => 'Place orders',
'orders:read:status' => 'Check order status',
]);
Passport::defaultScopes([
'user:read',
'orders:create',
]);
Призначення Областей для Токенів
Коли запитуються коди авторизації
Коли запитуєте токен доступу, використовуючи авторизаційний код, споживачі повинні вказати бажані області як параметр рядка запиту scope. Параметр scope повинен бути списком областей, розділених пробілами:
Route::get('/redirect', function () {
$query = http_build_query([
'client_id' => 'your-client-id',
'redirect_uri' => 'https://third-party-app.com/callback',
'response_type' => 'code',
'scope' => 'user:read orders:create',
]);
return redirect('https://passport-app.test/oauth/authorize?'.$query);
});
Коли Видаються Персональні Токени Доступу
Якщо ви видаєте токени особистого доступу, використовуючи метод createToken моделі App\Models\User, ви можете передати масив бажаних областей як другий аргумент до методу:
$token = $user->createToken('My Token', ['orders:create'])->accessToken;
Перевірка Областей
Passport включає два middleware, які можуть бути використані для перевірки того, що вхідний запит автентифікований за допомогою токена, якому було надано певну область дії.
Перевірити всі області застосування
The Laravel\Passport\Http\Middleware\CheckToken middleware може бути призначено до маршруту для перевірки, що токен доступу вхідного запиту має всі перелічені області:
use Laravel\Passport\Http\Middleware\CheckToken;
Route::get('/orders', function () {
// Токен доступу має як "orders:read", так і "orders:create" області...
})->middleware(['auth:api', CheckToken::using('orders:read', 'orders:create');
Перевірка на наявність будь-яких областей застосування
The Laravel\Passport\Http\Middleware\CheckTokenForAnyScope middleware може бути призначено до маршруту для перевірки, що токен доступу вхідного запиту має принаймні один з перелічених обсягів:
use Laravel\Passport\Http\Middleware\CheckTokenForAnyScope;
Route::get('/orders', function () {
// Токен доступу має або "orders:read", або "orders:create" область...
})->middleware(['auth:api', CheckTokenForAnyScope::using('orders:read', 'orders:create');
Перевірка Областей на Екземплярі Токена
Як тільки запит з аутентифікованим токеном доступу увійшов у ваш застосунок, ви все ще можете перевірити, чи має токен певну область, використовуючи метод tokenCan на аутентифікованому екземплярі App\Models\User:
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
if ($request->user()->tokenCan('orders:create')) {
// ...
}
});
Додаткові Методи Області
Метод scopeIds поверне масив усіх визначених ідентифікаторів / імен:
use Laravel\Passport\Passport;
Passport::scopeIds();
Метод scopes поверне масив усіх визначених областей як екземпляри Laravel\Passport\Scope:
Passport::scopes();
Метод scopesFor поверне масив екземплярів Laravel\Passport\Scope, що відповідають заданим ID / іменам:
Passport::scopesFor(['user:read', 'orders:create']);
Ви можете визначити, чи задано певну область, використовуючи метод hasScope:
Passport::hasScope('orders:create');
Аутентифікація SPA
Коли ви створюєте API, може бути надзвичайно корисно мати можливість споживати власний API з вашого JavaScript-застосунку. Такий підхід до розробки API дозволяє вашому власному застосунку споживати той самий API, який ви ділитеся зі світом. Той самий API може бути спожитий вашим веб-застосунком, мобільними застосунками, сторонніми застосунками та будь-якими SDK, які ви можете публікувати на різних менеджерах пакетів.
Зазвичай, якщо ви хочете використовувати ваш API з вашого JavaScript-застосунку, вам потрібно вручну надіслати токен доступу до застосунку і передавати його з кожним запитом до вашого застосунку. Однак, Passport включає middleware, яке може обробити це за вас. Все, що вам потрібно зробити, це додати middleware CreateFreshApiToken до групи middleware web у файлі bootstrap/app.php вашого застосунку:
use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
CreateFreshApiToken::class,
]);
})
Ви повинні переконатися, що CreateFreshApiToken middleware є останнім middleware у вашому стеку middleware.
Цей middleware додасть cookie laravel_token до ваших вихідних відповідей. Цей cookie містить зашифрований JWT, який Passport використовуватиме для автентифікації API-запитів з вашого JavaScript-застосунку. JWT має тривалість, що дорівнює значенню вашої конфігурації session.lifetime. Тепер, оскільки браузер автоматично надсилатиме cookie з усіма наступними запитами, ви можете робити запити до API вашого застосунку без явної передачі токена доступу:
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
Налаштування Імені Cookie
Якщо потрібно, ви можете налаштувати ім'я cookie laravel_token, використовуючи метод Passport::cookie. Зазвичай цей метод слід викликати з методу boot класу App\Providers\AppServiceProvider вашого застосунку:
/** * Ініціалізувати будь-які сервіси застосунку. */ public function boot(): void { Passport::cookie('custom_name'); }
Захист від CSRF
Коли ви використовуєте цей метод автентифікації, вам потрібно переконатися, що у ваших запитах включено дійсний заголовок CSRF токена. Стандартне JavaScript скелетування Laravel, яке включено в скелетний застосунок та всі стартові набори, містить екземпляр Axios, який автоматично використовуватиме зашифроване значення cookie XSRF-TOKEN для відправки заголовка X-XSRF-TOKEN у запитах з того ж джерела.
Якщо ви вирішите відправити заголовок X-CSRF-TOKEN замість X-XSRF-TOKEN, вам потрібно буде використовувати незашифрований токен, наданий csrf_token().
Події
Passport генерує події при видачі токенів доступу та оновлення. Ви можете слухати ці події, щоб видаляти або відкликати інші токени доступу у вашій базі даних:
| Назва Події |
|---|
Laravel\Passport\Events\AccessTokenCreated |
Laravel\Passport\Events\AccessTokenRevoked |
Laravel\Passport\Events\RefreshTokenCreated |
Тестування
Метод actingAs у Passport може бути використаний для вказівки поточного автентифікованого користувача, а також його областей дії. Перший аргумент, переданий методу actingAs, є екземпляром користувача, а другий - масивом областей дії, які повинні бути надані токену користувача:
use App\Models\User;
use Laravel\Passport\Passport;
test('orders can be created', function () {
Passport::actingAs(
User::factory()->create(),
['orders:create']
);
$response = $this->post('/api/orders');
$response->assertStatus(201);
});
use App\Models\User;
use Laravel\Passport\Passport;
public function test_orders_can_be_created(): void
{
Passport::actingAs(
User::factory()->create(),
['orders:create']
);
$response = $this->post('/api/orders');
$response->assertStatus(201);
}
Метод actingAsClient у Passport може бути використаний для вказівки поточного автентифікованого клієнта, а також його областей дії. Перший аргумент, переданий методу actingAsClient, є екземпляром клієнта, а другий - масивом областей дії, які повинні бути надані токену клієнта:
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
test('servers can be retrieved', function () {
Passport::actingAsClient(
Client::factory()->create(),
['servers:read']
);
$response = $this->get('/api/servers');
$response->assertStatus(200);
});
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
public function test_servers_can_be_retrieved(): void
{
Passport::actingAsClient(
Client::factory()->create(),
['servers:read']
);
$response = $this->get('/api/servers');
$response->assertStatus(200);
}
