XMLHttpRequest withCredentials POST, GET Авторизация с установкой куки и её отправкой в следующем запросе. Примеры запросов

Рассмотрим ситуацию с кроссдоменными запросами, где у вас должна быть в том числе авторизация. В этом примере мой бэк находится "где-то" а запросы будут выполняться иного домена - а именно с http://example.com

Собственно говоря, должен работать вот такой код (пример json-авторизации, с получением куки и её и использованием в следующем GET-запросе):

// первый запрос для авторизации
var domain = 'доменбэкэнда';
var xhr = new XMLHttpRequest();
xhr.open('POST', domain + 'api/v1/user/login', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.withCredentials = true; // НУЖНО, иначе пришедшая в ответ кука не установится
xhr.send('{"email":"myemail","password":"12345"}');

// второй запрос, запускайте после выполнения первого:

// запрос данных с использованием куки, из предыдущего ответа
var  domain = 'доменбэкэнда';

var xhr = new XMLHttpRequest();
xhr.open('GET', domain + 'api/v1/user/current', true);
xhr.withCredentials = true;
xhr.send();

где:

  • вместо
    var  domain = 'доменбэкэнда'; 

    напишите например:
    var domain = 'http://localhost:9121/';
    или иной ваш домен.

-- пример можно запустить в консоли браузера, сначала один запрос, а потом и второй.

Что должен делать фронт

  • По сути на чистом ли JS вы пишите или нет, не важно - главный момент состоит в том, чтобы в конечном итоге это транслировалось в опцию xhr.withCredentials = true не только для запросов чтения, использующих куки, но и для тех, что их получают (напр. метод авторизации выше - именно он должен получить от сервера заголовок Set-Cookie первый раз). Без этой опции куки из ответа сервера будут проигнорированы.

Что должен отдавать бэкэнд

На бэкэнде убедитесь, что (напр. смотря на приходящие заголовки в консоли браузера):

  • Access-Control-Allow-Origin отдает тот домен, с которого идет запрос (или вообще любой, или что запрашиваете данные с чужого, но разрешенного домен - см. настройку в PHP)
    Например:

    Access-Control-Allow-Origin: http://example.com

    Примечание: В Сети указано, что нельзя отдавать просто * если вы используете Access-Control-Allow-Credentials: true (см. далее)

  • Access-Control-Allow-Credentials должен быть установлен сервером в true
    Access-Control-Allow-Credentials: true
  • Проверьте на бэкэнде как вы устанавливаете куки в своем приложении, для ajax-запросов не подойдет SameSite: lux, в приложении нужно выставить
    SameSite: none

    НО: хотя на момент написания заметки это и работает, но в ближайшем будущем браузеры будут требовать для куков с опцией SameSite: none дополнительно ставить флаг Secure что автоматически потребует от вас запускать бэкэнд через https.

Пример заголовков, присываемых сервером, с которыми пример JS выше отработа хорошо для обоих запросов (вы тоже можете потестировать запросы к вашему приложению открыв веб-консоль инструментов разработчика в браузере на сайте example.com):

HTTP/1.1 200 OK
Date: Mon, 29 Jun 2020 10:50:36 GMT
Server: Apache/2.4.29 (Ubuntu)
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS
Access-Control-Max-Age: 1
Access-Control-Allow-Headers: Content-Type, Content-Range, Content-Disposition, Content-Description, X-AUTH-TOKEN
Cache-Control: max-age=0, must-revalidate, private
Content-Length: 518
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json

Для настройки севера, я использовал такие идеи/фрагменты кода.

withCredentials в JQuery

Как и в чистом javascript, withCredentials: true надо передавать в JQuery не только для использования куков запросом, но и при получении их в запросе авторизации, иначе они будут проигнорированы (т.е. фактически во всех ваших запросах, если они идут через ajax
).

Включается опция по схеме:

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
      withCredentials: true
   }
});

Источники/что почитать