Sanctum или Passport: как выбрать авторизацию в Laravel
SPA на Vue, мобильное приложение и сторонние клиенты требуют разной авторизации. Разбираем, где Sanctum достаточно, а где без OAuth2 из Passport не обойтись, и чем платишь за каждый выбор.

Вопрос «Sanctum или Passport» встаёт в Laravel-проекте на старте, когда фронтенд отделяется от бэкенда. Обе библиотеки от команды Laravel, обе решают аутентификацию для API, но они не взаимозаменяемы. Sanctum — лёгкий пакет для токенов и куки-сессий SPA. Passport — полноценный сервер OAuth2 на базе league/oauth2-server. Выбрать не тот инструмент — значит либо тащить в проект сервер авторизации, который вам не нужен, либо через полгода упереться в то, что Sanctum не выдаёт refresh-токены и не умеет authorization code flow.
Разберём по задачам: что выигрываете и чем платите в каждом случае. Ориентируюсь на Laravel 11.x, Sanctum 4.x и Passport 12.x — проверяйте версии в composer.json, поведение между мажорными релизами Passport заметно менялось.
Что именно делает каждый пакет
Sanctum даёт два независимых механизма. Первый — токены API: строка вида 1|xxxxx, которую вы отдаёте клиенту и он шлёт в заголовке Authorization: Bearer. Токены хранятся в таблице personal_access_tokens, у каждого можно задать список способностей (abilities). Второй — SPA-аутентификация через обычные Laravel-сессии и куки: фронтенд на том же домене (или поддомене) логинится по сессии, а Sanctum защищает от CSRF.
Passport реализует спецификацию OAuth2: authorization code grant, client credentials, password grant (помечен deprecated в спецификации, но в пакете есть), refresh-токены. Токены — подписанные JWT. Это то, что нужно, когда к вашему API подключаются сторонние приложения, которым пользователь даёт доступ, не отдавая пароль.
Правило первого приближения: если все клиенты API — ваши собственные (ваш SPA, ваше мобильное приложение), берите Sanctum. OAuth2 нужен тогда, когда клиент — не вы.
Сноска: OAuth2 authorization code flow
Authorization code flow — сценарий, где пользователь на странице вашего сервиса нажимает «Разрешить доступ приложению X», сервер выдаёт временный код, а приложение обменивает его на токен доступа. Так работает «Войти через…» и интеграции, где пользователь не должен передавать пароль стороннему сервису. Sanctum этого не умеет by design.
Четыре типовых сценария
- SPA на том же домене (Vue/React + Laravel-бэкенд на api.вашсайт.ru). Здесь Sanctum в режиме куки-сессий — эталонный выбор. Никаких токенов в localStorage, авторизация через httpOnly-куку, защита от XSS-кражи токена из коробки. Passport тут избыточен.
- Мобильное приложение к своему API. Sanctum с персональными токенами: приложение при логине получает токен, хранит в secure storage устройства, шлёт в Bearer. Refresh-логику при необходимости пишете сами — Sanctum ротацию не делает.
- Сторонние разработчики подключаются к вашему API. Нужен OAuth2 — значит Passport. Пользователь авторизует чужое приложение, вы контролируете scopes и можете отозвать доступ конкретному клиенту.
- Микросервисы между собой. Client credentials grant из Passport, либо простые Sanctum-токены с abilities, если сервисы ваши и вам не нужна ротация. Для машина-машина взаимодействия часто хватает Sanctum.
Сравнение по существенным параметрам
| Параметр | Sanctum | Passport |
|---|---|---|
| Формат токена | Случайная строка в БД | Подписанный JWT |
| SPA через куки | Да, основной режим | Нет |
| OAuth2 (authorization code) | Нет | Да |
| Refresh-токены | Нет | Да |
| Права доступа | Abilities (простой список) | Scopes |
| Проверка токена | Запрос к БД | Проверка подписи, БД не обязательна |
| Установка ключей | Не нужно | passport:keys обязательно |
| Порог входа | Низкий | Средний |
Строка про проверку токена важнее, чем кажется. Sanctum-токен на каждый запрос читается из базы — при высокой нагрузке это лишний SELECT. JWT из Passport проверяется по подписи без обращения к БД, но обратная сторона — отозвать такой токен мгновенно нельзя без отдельной таблицы отозванных токенов (Passport её ведёт, но это снова обращение к хранилищу).
Когда что выбирать: критерий, а не «зависит от задачи»
- Клиент и сервер на одном домене или поддоменах, фронтенд ваш — Sanctum, куки-режим.
- Мобильный или desktop-клиент только ваш, ротация токенов не критична — Sanctum, токены.
- Нужно, чтобы пользователь давал доступ чужому приложению — Passport, других вариантов из коробки нет.
- Требуются refresh-токены и короткоживущие access-токены по правилам безопасности заказчика — Passport.
- Сомневаетесь и внешних клиентов пока нет — начните с Sanctum. Мигрировать на Passport позже проще, чем поддерживать неиспользуемый OAuth2-сервер.
Как это выглядит в коде
Выдача токена в Sanctum после проверки логина и пароля. Метод createToken возвращает объект, у которого plainTextToken — единственный момент, когда вы видите токен в открытом виде.
// Laravel 11.x, Sanctum 4.x — контроллер логина
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Неверные данные'], 401);
}
// ['orders:read'] — ability, ограничивает права токена
$token = $user->createToken('mobile-app', ['orders:read']);
return response()->json([
'token' => $token->plainTextToken,
]);
}Защита маршрута и проверка ability на стороне API:
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::get('/orders', function (Request $request) {
// проверяем, что у токена есть право на чтение заказов
if (! $request->user()->tokenCan('orders:read')) {
abort(403);
}
return Order::where('user_id', $request->user()->id)->get();
});
});В Passport логика клиента иная: вы не выдаёте токен из своего контроллера логина, а поднимаете OAuth2-эндпоинты. Точный набор маршрутов и способ их регистрации в Passport 12.x смотрите в официальной документации — API регистрации маршрутов между версиями менялся, и подставлять по памяти вызовы я не буду.
Что ломается на практике
- SPA-режим Sanctum и CORS. Куки-сессия работает, только если фронт и API на одном домене верхнего уровня. Разные домены (frontend.com и api.backend.ru) — куки не поедут, и вы получите 401 после успешного логина. Нужно либо свести к поддоменам, либо переходить на токены.
- Забытый
SANCTUM_STATEFUL_DOMAINS. Если этот параметр в конфиге не включает домен вашего SPA, Sanctum не считает запрос «сессионным» и снова отдаёт 401. Частая причина «логин прошёл, а данные не отдаёт». - Passport без ключей. Забыли выполнить генерацию ключей после установки — получите ошибку при попытке выдать токен. Ключи не коммитятся в репозиторий и должны генерироваться на каждом окружении.
- Отзыв JWT. Ожидание, что удаление записи в БД мгновенно инвалидирует токен Passport, неверно: пока токен не истёк, подпись валидна. Планируйте короткое время жизни access-токена.
Что делать дальше
Определите домены клиентов и наличие внешних потребителей API — это решает выбор за вас на 90%. Если остановились на Sanctum, сразу пропишите SANCTUM_STATEFUL_DOMAINS и настройте CORS до того, как фронтенд начнёт стучаться. Если выбрали Passport, продумайте время жизни access-токена и стратегию refresh, а также как будете отзывать доступ клиентам. В обоих случаях в проде смотрите на количество запросов к таблице токенов — это первый показатель, который упрётся в потолок при росте нагрузки.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Laravel Sanctum — официальная документация, Laravel Passport — официальная документация
Частые вопросы
Можно ли использовать Sanctum и Passport в одном проекте?
А если у меня Laravel другой версии, эти примеры сработают?
Sanctum не отдаёт данные после успешного логина в SPA — что проверить?
Нужен ли Passport, если я хочу «Войти через мой сервис» для сторонних сайтов?
Что безопаснее хранить на фронтенде — токен или сессионную куку?
Как быстро отозвать доступ конкретному пользователю?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.