Postman и Sanctum

# Проблема

В Postman не работает SPA аутентификация на Sanctum. Маршруты с auth:sanctum возвращают 401 статус, даже после успешного логина. В браузере аутентификация работает.

# Причина

Для корректной работы "SPA способа" необходимы некоторые условия, а именно:

С первым пунктом обычно проблем не возникает, а вот про Referer/Origin знают не все, хотя это есть в документации. Дело в том, что механизм Sanctum подключается вариативно с помощью middleware \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful

И будут ли применены к запросу другие middleware зависит от метода fromFrontend, который получает домен из заголовков Referer или Origin и ищет их среди разрешенных доменов в конфиге sanctum.stateful или SANCTUM_STATEFUL_DOMAINS в env.

Таким образом, если нет заголовков или домен не соответствует разрешенным, то middleware не подключается, а значит не стартует сессия и не работает аутентификация.

# Решение

Необходимо отправлять в Postman заголовок Referer или Origin с разрешенным доменом (на котором находится SPA), указанным в SANCTUM_STATEFUL_DOMAINS.

Например, Origin: https://example.com

# Настройка Postman

Выполнять запросы вручную каждый раз утомительно, необходим предварительный запрос на /sanctum/csrf-cookie и передавать куки в следующем запросе.

Вместо этого можно "автоматизировать" запросы в Postman. Для этого необходимо добавить скрипт на вкладке Pre-request Script.

Обрати внимание, что используется переменная окружения API_URL, её необходимо создать в Postman , либо заменить на свой URL прямо в скрипте.

1if (pm.request.method !== 'GET') {
2 if (pm.environment.get('XSRF-TOKEN')) {
3 pm.request.headers.upsert({
4 key: 'x-xsrf-token',
5 value: pm.environment.get('XSRF-TOKEN'),
6 })
7 } else {
8 pm.sendRequest(pm.environment.get('API_URL') + '/sanctum/csrf-cookie', function(error, response) {
9 pm.expect(error).to.equal(null)
10 pm.expect(response).to.have.property('code', 204)
11 pm.expect(response).to.have.property('status', 'No Content')
12 
13 let xsrfCookie = response.headers.find(header => header.key.toLowerCase() === 'set-cookie' && header.value.includes('XSRF-TOKEN'))
14 
15 if (xsrfCookie) {
16 let xsrfToken = decodeURIComponent(xsrfCookie.value.split(';')[0].split('=')[1])
17 
18 pm.request.headers.upsert({
19 key: 'x-xsrf-token',
20 value: xsrfToken,
21 })
22 
23 pm.environment.set('XSRF-TOKEN', xsrfToken)
24 }
25 })
26 }
27}
highlight by torchlight.dev

Не забудь передать заголовки Accept: application/json и Origin с адресом, где находится frontend приложение. Так же убедись, что домен добавлен в SANCTUM_STATEFUL_DOMAINS.

Обрати внимание, что SPA и API приложения могут находиться на разных поддоменах, поэтому в Origin необходимо указывать именно адрес SPA, а не API.