Auth-токен остаётся в cookie
Логин, регистрация, logout и текущий пользователь живут под /api. Токен это httpOnly auth_token. Он не попадает в localStorage и не едет в теле JSON.
Публичный API авторизации это четыре маршрута: /api/login, /api/register, /api/logout и /api/user. Sanctum пишет сессию в httpOnly cookie auth_token. Логин больше не возвращает поле token. На клиенте никто не пишет токен в localStorage.
Если скрипт на origin читает сессию, XSS её унесёт. httpOnly cookie из JavaScript не достать. Та же инъекция не скопирует auth_token. Logout удаляет токен на сервере и cookie, поэтому скопированная cookie перестаёт работать сразу.
CSRF на каждую запись
Небезопасные методы требуют CSRF-cookie и парный заголовок. Фронтенд один раз получает cookie, затем шлёт заголовок на каждую запись. Отсутствующий или протухший заголовок даёт 419, а не ошибку валидации, похожую на неверный пароль.
Админ-маршруты сидят за той же cookie-сессией. Истёкшая сессия ведёт на логин, а не на сломанную страницу. Маршрут логина ротирует id сессии.
Что фиксируют тесты
Четыре случая раньше регрессировали. В JSON логина нет токена. Флаг httpOnly на auth_token на месте. Запись без CSRF-заголовка отклонена. /api/user отдаёт 401 после logout.
Правило достаточно короткое, чтобы его держать. Токены живут в cookie. Тела JSON несут поля пользователя, не секреты. Если новому экрану нужен текущий пользователь, он зовёт /api/user с credentials. Второй склад он не заводит.
