Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти
Інше
Події Про нас

Laravel Sanctum

Вступ

Laravel Sanctum надає легку як пір'їнка систему автентифікації для SPA (односторінкових застосунків), мобільних застосунків і простих API на токенах. Sanctum дозволяє кожному користувачеві вашого застосунку генерувати кілька API-токенів для свого облікового запису. Цим токенам можна надати можливості / скопи, які визначають, які дії дозволено виконувати з токеном.

Як це працює

Laravel Sanctum існує, щоб розв'язати дві окремі проблеми. Обговорімо кожну з них, перш ніж заглиблюватися в бібліотеку.

API-токени

По-перше, Sanctum - це простий пакет, яким ви можете видавати API-токени своїм користувачам без складнощів OAuth. Ця можливість надихнута GitHub та іншими застосунками, які видають «персональні токени доступу». Уявіть, наприклад, що в «налаштуваннях облікового запису» вашого застосунку є екран, де користувач може згенерувати API-токен. Sanctum дозволяє генерувати такі токени й керувати ними. Зазвичай вони мають дуже довгий термін дії (роки), але користувач може будь-коли відкликати їх вручну.

Laravel Sanctum реалізує цю можливість, зберігаючи API-токени користувачів в одній таблиці бази даних і автентифікуючи вхідні HTTP-запити через заголовок Authorization, який має містити дійсний API-токен.

Автентифікація SPA

По-друге, Sanctum пропонує простий спосіб автентифікувати односторінкові застосунки (SPA), яким треба спілкуватися з API на Laravel. Такі SPA можуть жити в тому самому репозиторії, що й ваш застосунок Laravel, або в цілком окремому - наприклад, SPA, створений на Next.js чи Nuxt.

Для цієї можливості Sanctum не використовує жодних токенів. Натомість Sanctum спирається на вбудовані сервіси сесійної автентифікації Laravel на основі cookie. Зазвичай для цього Sanctum використовує гард автентифікації web. Це дає переваги захисту від CSRF, сесійної автентифікації, а також захищає від витоку облікових даних через XSS.

Sanctum намагатиметься автентифікувати через cookie лише тоді, коли вхідний запит надходить із фронтенду вашого власного SPA. Перевіряючи вхідний HTTP-запит, Sanctum спершу шукає cookie автентифікації, а якщо його немає - перевіряє заголовок Authorization на наявність дійсного API-токена.

Цілком нормально користуватися Sanctum лише для автентифікації через API-токени або лише для автентифікації SPA. Використання Sanctum не зобов'язує вас застосовувати обидві його можливості.

Встановлення

Ви можете встановити Laravel Sanctum артизан-командою install:api:

php artisan install:api

Далі, якщо ви плануєте автентифікувати через Sanctum ваш SPA, зверніться до розділу Автентифікація SPA цієї документації.

Конфігурація

Перевизначення стандартних моделей

Хоча зазвичай це не потрібно, ви вільні розширити модель PersonalAccessToken, яку Sanctum використовує всередині:

use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;

class PersonalAccessToken extends SanctumPersonalAccessToken
{
    // ...
}

Далі ви можете вказати Sanctum використовувати вашу модель через метод usePersonalAccessTokenModel, який надає Sanctum. Зазвичай цей метод викликають у методі boot файлу AppServiceProvider вашого застосунку:

use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}

Автентифікація через API-токени

Вам не слід автентифікувати власний SPA через API-токени. Натомість скористайтеся вбудованими можливостями автентифікації SPA у Sanctum.

Видача API-токенів

Sanctum дозволяє видавати API-токени / персональні токени доступу, якими можна автентифікувати API-запити до вашого застосунку. Роблячи запити з API-токеном, передавайте його в заголовку Authorization як токен Bearer.

Щоб почати видавати токени користувачам, ваша модель User має використовувати трейт Laravel\Sanctum\HasApiTokens:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Щоб видати токен, скористайтеся методом createToken. Метод createToken повертає екземпляр Laravel\Sanctum\NewAccessToken. API-токени хешуються через SHA-256 перед збереженням у вашій базі даних, але ви можете отримати значення токена відкритим текстом через властивість plainTextToken екземпляра NewAccessToken. Це значення слід показати користувачеві одразу після створення токена:

use Illuminate\Http\Request;

Route::post('/tokens/create', function (Request $request) {
    $token = $request->user()->createToken($request->token_name);

    return ['token' => $token->plainTextToken];
});

Ви можете дістатися всіх токенів користувача через зв'язок Eloquent tokens, який надає трейт HasApiTokens:

foreach ($user->tokens as $token) {
    // ...
}

Можливості токенів

Sanctum дозволяє призначати токенам «можливості» (abilities). Можливості слугують схожій меті, що й «скопи» в OAuth. Ви можете передати масив рядків-можливостей другим аргументом до методу createToken:

return $user->createToken('token-name', ['server:update'])->plainTextToken;

Обробляючи вхідний запит, автентифікований через Sanctum, ви можете визначити, чи має токен задану можливість, методами tokenCan чи tokenCant:

if ($user->tokenCan('server:update')) {
    // ...
}

if ($user->tokenCant('server:update')) {
    // ...
}

Middleware для можливостей токенів

Sanctum також містить два middleware, якими можна перевірити, що вхідний запит автентифіковано токеном із заданою можливістю. Для початку визначте у файлі bootstrap/app.php вашого застосунку такі аліаси middleware:

use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'abilities' => CheckAbilities::class,
        'ability' => CheckForAnyAbility::class,
    ]);
})

middleware abilities можна призначити маршруту, щоб перевірити, що токен вхідного запиту має усі перелічені можливості:

Route::get('/orders', function () {
    // Token has both "check-status" and "place-orders" abilities...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);

middleware ability можна призначити маршруту, щоб перевірити, що токен вхідного запиту має хоча б одну з перелічених можливостей:

Route::get('/orders', function () {
    // Token has the "check-status" or "place-orders" ability...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);

Запити, ініційовані власним UI

Для зручності метод tokenCan завжди повертатиме true, якщо вхідний автентифікований запит надійшов із вашого власного SPA й ви користуєтеся вбудованою автентифікацією SPA у Sanctum.

Проте це не означає, що ваш застосунок зобов'язаний дозволити користувачеві виконати дію. Зазвичай політики авторизації вашого застосунку визначатимуть і те, чи надано токену дозвіл виконувати ці можливості, і те, чи має право виконати дію сам користувач.

Наприклад, якщо уявити застосунок для керування серверами, це може означати перевірку того, що токену дозволено оновлювати сервери і що сервер належить користувачеві:

return $request->user()->id === $server->user_id &&
       $request->user()->tokenCan('server:update')

Спершу може здатися дивним, що метод tokenCan можна викликати й він завжди повертає true для запитів, ініційованих власним UI; проте зручно завжди мати змогу припускати, що API-токен доступний і його можна перевірити методом tokenCan. Завдяки такому підходу ви можете завжди викликати метод tokenCan у політиках авторизації вашого застосунку, не переймаючись тим, чи запит надійшов з UI вашого застосунку, чи його ініціював хтось зі сторонніх споживачів вашого API.

Захист маршрутів

Щоб захистити маршрути так, аби всі вхідні запити мали бути автентифікованими, додайте гард автентифікації sanctum до ваших захищених маршрутів у файлах routes/web.php та routes/api.php. Цей гард гарантуватиме, що вхідні запити або автентифіковані як stateful-запити через cookie, або містять заголовок із дійсним API-токеном, якщо запит надійшов від третьої сторони.

Ви можете замислитися, чому ми радимо автентифікувати гардом sanctum і маршрути у файлі routes/web.php. Пам'ятайте: Sanctum спершу спробує автентифікувати вхідні запити через звичайний сесійний cookie автентифікації Laravel. Якщо цього cookie немає, Sanctum спробує автентифікувати запит токеном із заголовка Authorization. До того ж автентифікація всіх запитів через Sanctum гарантує, що ми завжди зможемо викликати метод tokenCan на екземплярі поточного автентифікованого користувача:

use Illuminate\Http\Request;

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

Відкликання токенів

Ви можете «відкликати» токени, видаливши їх зі своєї бази даних через зв'язок tokens, який надає трейт Laravel\Sanctum\HasApiTokens:

// Revoke all tokens...
$user->tokens()->delete();

// Revoke the token that was used to authenticate the current request...
$request->user()->currentAccessToken()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();

Термін дії токенів

За замовчуванням токени Sanctum ніколи не спливають, і їх можна знеструмити лише відкликанням. Проте якщо ви хочете задати час дії API-токенів вашого застосунку, зробіть це через опцію конфігурації expiration у вашому конфігураційному файлі sanctum. Ця опція визначає кількість хвилин, після яких виданий токен вважатиметься простроченим:

'expiration' => 525600,

Якщо ви хочете задати термін дії кожного токена окремо, передайте його третім аргументом до методу createToken:

return $user->createToken(
    'token-name', ['*'], now()->plus(weeks: 1)
)->plainTextToken;

Якщо ви налаштували термін дії токенів для свого застосунку, вам, можливо, варто також запланувати завдання, яке прибиратиме прострочені токени. На щастя, Sanctum містить артизан-команду sanctum:prune-expired саме для цього. Наприклад, ви можете налаштувати заплановане завдання, яке видалятиме всі записи токенів, прострочених щонайменше 24 години:

use Illuminate\Support\Facades\Schedule;

Schedule::command('sanctum:prune-expired --hours=24')->daily();

Автентифікація SPA

Sanctum також існує, щоб дати простий спосіб автентифікувати односторінкові застосунки (SPA), яким треба спілкуватися з API на Laravel. Такі SPA можуть жити в тому самому репозиторії, що й ваш застосунок Laravel, або в цілком окремому.

Для цієї можливості Sanctum не використовує жодних токенів. Натомість Sanctum спирається на вбудовані сервіси сесійної автентифікації Laravel на основі cookie. Такий підхід до автентифікації дає переваги захисту від CSRF, сесійної автентифікації, а також захищає від витоку облікових даних через XSS.

Щоб автентифікація працювала, ваш SPA та API мають бути на одному домені верхнього рівня. Проте вони можуть бути на різних піддоменах. Крім того, надсилайте із запитом заголовок Accept: application/json і заголовок Referer чи Origin.

Конфігурація

Налаштування власних доменів

Спершу вам треба вказати, з яких доменів ваш SPA робитиме запити. Налаштувати ці домени можна через опцію конфігурації stateful у вашому конфігураційному файлі sanctum. Ця настройка визначає, які домени підтримуватимуть «stateful»-автентифікацію через сесійні cookie Laravel під час запитів до вашого API.

Щоб допомогти вам налаштувати власні stateful-домени, Sanctum надає дві допоміжні функції, які можна включити до конфігурації. Перша, Sanctum::currentApplicationUrlWithPort(), поверне поточний URL застосунку зі змінної оточення APP_URL, а Sanctum::currentRequestHost() вставить до списку stateful-доменів плейсхолдер, який під час виконання буде замінено на хост із поточного запиту, - тож усі запити з тим самим доменом вважатимуться stateful.

Якщо ви звертаєтеся до застосунку за URL із портом (127.0.0.1:8000), обов'язково вкажіть номер порту разом із доменом.

Middleware Sanctum

Далі вам слід указати Laravel, що вхідні запити з вашого SPA можуть автентифікуватися через сесійні cookie Laravel, водночас дозволивши запитам від третіх сторін чи мобільних застосунків автентифікуватися через API-токени. Легко зробити це можна викликом методу middleware statefulApi у файлі bootstrap/app.php вашого застосунку:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->statefulApi();
})

CORS і cookie

Якщо у вас виникають проблеми з автентифікацією у вашому застосунку з SPA, що працює на окремому піддомені, ви, найімовірніше, неправильно налаштували CORS (Cross-Origin Resource Sharing) або сесійні cookie.

Конфігураційний файл config/cors.php за замовчуванням не публікується. Якщо вам треба налаштувати опції CORS у Laravel, опублікуйте повний конфігураційний файл cors артизан-командою config:publish:

php artisan config:publish cors

Далі переконайтеся, що конфігурація CORS вашого застосунку повертає заголовок Access-Control-Allow-Credentials зі значенням True. Це можна зробити, встановивши опцію supports_credentials у вашому файлі config/cors.php у значення true.

Крім того, увімкніть опції withCredentials та withXSRFToken на глобальному екземплярі axios вашого застосунку. Зробити це можна у файлі resources/js/app.js. Якщо ви не користуєтеся Axios для HTTP-запитів із фронтенду, виконайте рівнозначну конфігурацію у своєму HTTP-клієнті:

axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;

Нарешті, переконайтеся, що конфігурація домену сесійних cookie вашого застосунку підтримує будь-який піддомен вашого кореневого домену. Для цього додайте до домену провідну крапку . у вашому конфігураційному файлі config/session.php:

'domain' => '.domain.com',

Автентифікація

Захист від CSRF

Щоб автентифікувати ваш SPA, сторінка «входу» вашого SPA має спершу зробити запит до ендпоїнта /sanctum/csrf-cookie, щоб ініціалізувати захист від CSRF:

axios.get('/sanctum/csrf-cookie').then(response => {
    // Login...
});

Під час цього запиту Laravel установить cookie XSRF-TOKEN з поточним CSRF-токеном. Далі цей токен слід декодувати з URL і передавати в заголовку X-XSRF-TOKEN у наступних запитах - деякі HTTP-клієнти на кшталт Axios чи Angular HttpClient зроблять це за вас автоматично. Якщо ваша JavaScript-бібліотека не встановлює це значення сама, вам доведеться вручну задати заголовок X-XSRF-TOKEN так, щоб він відповідав URL-декодованому значенню cookie XSRF-TOKEN, встановленого цим маршрутом.

Вхід

Коли захист від CSRF ініціалізовано, зробіть запит POST до маршруту /login вашого застосунку Laravel. Цей маршрут /login можна реалізувати вручну або через headless-пакет автентифікації на кшталт Laravel Fortify.

Якщо запит на вхід успішний, вас буде автентифіковано, і наступні запити до маршрутів вашого застосунку автоматично автентифікуватимуться через сесійний cookie, який застосунок Laravel видав вашому клієнту. До того ж, оскільки ваш застосунок уже звернувся до маршруту /sanctum/csrf-cookie, наступні запити мають автоматично отримувати захист від CSRF, доки ваш JavaScript HTTP-клієнт надсилає значення cookie XSRF-TOKEN у заголовку X-XSRF-TOKEN.

Звісно, якщо сесія користувача спливе через брак активності, наступні запити до застосунку Laravel можуть отримати HTTP-помилку 401 чи 419. У такому разі перенаправте користувача на сторінку входу вашого SPA.

Оскільки такий підхід до автентифікації SPA базується на сесіях, ви можете користуватися стандартними сервісами автентифікації Laravel, зокрема функціональністю «запам'ятати мене».

Ви вільні написати власний ендпоїнт /login; проте переконайтеся, що він автентифікує користувача через стандартні сервіси сесійної автентифікації, які надає Laravel. Зазвичай це означає використання гарда автентифікації web.

Захист маршрутів

Щоб захистити маршрути так, аби всі вхідні запити мали бути автентифікованими, додайте гард автентифікації sanctum до ваших API-маршрутів у файлі routes/api.php. Цей гард гарантуватиме, що вхідні запити або автентифіковані як stateful-запити з вашого SPA, або містять заголовок із дійсним API-токеном, якщо запит надійшов від третьої сторони:

use Illuminate\Http\Request;

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

Авторизація приватних каналів бродкастингу

Якщо вашому SPA треба автентифікуватися в приватних каналах чи каналах присутності, приберіть запис channels з методу withRouting у файлі bootstrap/app.php вашого застосунку. Натомість викличте метод withBroadcasting, щоб указати правильне middleware для маршрутів бродкастингу вашого застосунку:

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        // ...
    )
    ->withBroadcasting(
        __DIR__.'/../routes/channels.php',
        ['prefix' => 'api', 'middleware' => ['api', 'auth:sanctum']],
    )

Далі, щоб запити авторизації Pusher були успішними, вам треба передати власний authorizer для Pusher під час ініціалізації Laravel Echo. Це дозволить вашому застосунку налаштувати Pusher на використання екземпляра axios, належно налаштованого для міждоменних запитів:

window.Echo = new Echo({
    broadcaster: "pusher",
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    encrypted: true,
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    authorizer: (channel, options) => {
        return {
            authorize: (socketId, callback) => {
                axios.post('/api/broadcasting/auth', {
                    socket_id: socketId,
                    channel_name: channel.name
                })
                .then(response => {
                    callback(false, response.data);
                })
                .catch(error => {
                    callback(true, error);
                });
            }
        };
    },
})

Автентифікація мобільних застосунків

Ви також можете автентифікувати токенами Sanctum запити вашого мобільного застосунку до вашого API. Процес автентифікації запитів мобільного застосунку схожий на автентифікацію сторонніх API-запитів; проте є невеликі відмінності в тому, як ви видаватимете API-токени.

Видача API-токенів

Для початку створіть маршрут, який приймає пошту / ім'я користувача, пароль та ім'я пристрою, а потім обмінює ці облікові дані на новий токен Sanctum. «Ім'я пристрою», передане цьому ендпоїнту, потрібне лише для інформації й може бути будь-яким. Загалом ім'я пристрою має бути таким, яке користувач упізнає, - наприклад, «Nuno's iPhone 17».

Зазвичай ви робитимете запит до цього ендпоїнта з екрана «входу» вашого мобільного застосунку. Ендпоїнт поверне API-токен відкритим текстом, який потім можна зберегти на мобільному пристрої й використовувати для наступних API-запитів:

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

Route::post('/sanctum/token', function (Request $request) {
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
        'device_name' => 'required',
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

    return $user->createToken($request->device_name)->plainTextToken;
});

Коли мобільний застосунок робить із цим токеном API-запит до вашого застосунку, він має передавати токен у заголовку Authorization як токен Bearer.

Видаючи токени для мобільного застосунку, ви так само вільні вказати можливості токенів.

Захист маршрутів

Як уже описано раніше, ви можете захистити маршрути так, аби всі вхідні запити мали бути автентифікованими, додавши до них гард автентифікації sanctum:

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

Відкликання токенів

Щоб дозволити користувачам відкликати API-токени, видані мобільним пристроям, ви можете вивести їх за іменами разом із кнопкою «Revoke» у розділі «налаштування облікового запису» інтерфейсу вашого вебзастосунку. Коли користувач натисне «Revoke», ви можете видалити токен із бази даних. Пам'ятайте: до API-токенів користувача можна дістатися через зв'язок tokens, який надає трейт Laravel\Sanctum\HasApiTokens:

// Revoke all tokens...
$user->tokens()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();

Тестування

Під час тестування метод Sanctum::actingAs дозволяє автентифікувати користувача й указати, які можливості слід надати його токену:

use App\Models\User;
use Laravel\Sanctum\Sanctum;

test('task list can be retrieved', function () {
    Sanctum::actingAs(
        User::factory()->create(),
        ['view-tasks']
    );

    $response = $this->get('/api/task');

    $response->assertOk();
});
use App\Models\User;
use Laravel\Sanctum\Sanctum;

public function test_task_list_can_be_retrieved(): void
{
    Sanctum::actingAs(
        User::factory()->create(),
        ['view-tasks']
    );

    $response = $this->get('/api/task');

    $response->assertOk();
}

Якщо ви хочете надати токену всі можливості, включіть * до списку можливостей, переданого методу actingAs:

Sanctum::actingAs(
    User::factory()->create(),
    ['*']
);