Разделение внешнего интерфейса и внутреннего интерфейса: Safari не хранит файлы cookie из API, который размещен в отдельном домене, чем его клиент Frontend SPA - PullRequest
0 голосов
/ 21 сентября 2018

У меня есть настройка, которая, насколько я могу судить, в настоящее время довольно распространена: внутренний REST API, который живет в своем собственном домене, скажем, myapi.com, и одностраничное веб-приложение, которое обслуживается где-то еще, скажем,myapp.com.

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

Внутренний API-интерфейс использует файлы cookie для хранения данных сеанса для некоторых разрешенных источников, среди которых myapp.com.Это сделано для того, чтобы иметь безопасную шину для передачи и хранения данных аутентификации, не беспокоясь об этом на стороне клиента.

В Chrome, Opera и Firefox это работает просто отлично: для проверки подлинности выполняется вызов APIпользователь, файлы cookie возвращаются и сохраняются в браузере, чтобы затем их можно было отправить вместе со следующим вызовом.

С другой стороны, Safari получает файлы cookie, но отказывается их хранить:

Auth call

First call

Я подозреваю, что Safari рассматривает домен API как сторонний домен cookie и поэтому блокирует файлы cookieот хранения.

Это ожидаемое поведение в Safari?Если да, то каковы лучшие методы, чтобы обойти это?

1 Ответ

0 голосов
/ 21 сентября 2018

Сохраняя традицию , отвечая на ваш собственный вопрос на этот вопрос.

TL; DR - это желаемое поведение в Safari.Единственный способ обойти это - привести пользователя на веб-страницу, размещенную на домене API (myapi.com в вопросе), и установить оттуда cookie-файл - что угодно, вы можете написать небольшое стихотворение в cookie-файле, если хотите.

После того, как это будет сделано, домен будет добавлен в «белый список», и Safari будет любезен с вами и будет устанавливать ваши cookie-файлы при любом последующем звонке, даже если он поступает от клиентов из разных доменов.

Этоподразумевает, что вы можете оставить свою логику аутентификации нетронутой и просто ввести тупую конечную точку, которая установит для вас «начальный» файл cookie.В моем приложении на Ruby это выглядит следующим образом:

class ServiceController < ActionController::Base
  def seed_cookie
    cookies[:s] = {value: 42, expires: 1.week, httponly: true} # value can be anything at all
    render plain: "Checking your browser"
  end
end

Клиентская сторона, вы можете проверить , если браузер, выполняющий запрос, Safari и отложить логику входа в систему после того, как появилось это уродливое всплывающее окноБыло открыто:

const doLogin = () => {
  if(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
    const seedCookie = window.open(`http://myapi.com/seed_cookie`, "s", "width=1, height=1, bottom=0, left=0, toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no")
    setTimeout(() => {
      seedCookie.close();
      // your login logic;
    }, 500);
  } else {
    // your login logic;
  }
}

ОБНОВЛЕНИЕ : Приведенное выше решение отлично работает для входа пользователя, т.е. оно корректно «заносит в белый список» домен API для текущего сеанса браузера.

К сожалению, однако, похоже, что пользователь, обновляющий страницу, вернет браузер в исходное состояние, когда сторонние куки для домена API заблокированы.

Я нашел хороший способ справиться со случаемобновление окна должно обнаружить его в javascript при загрузке страницы и перенаправить пользователя на конечную точку API, которая делает то же, что и выше, просто чтобы затем перенаправить пользователя на исходный URL, на который он переходил (страница обновляется):

if(performance.navigation.type == 1 && /^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
  window.location.replace(`http://myapi.com/redirect_me`);
}

Чтобы усложнить ситуацию, оказывается, что Safari не будет хранить куки, если HTTP-статус ответа 30X (повторнонепосредственный).Таким образом, Safari-дружественное решение включает в себя настройку файлов cookie и возврат ответа 200 вместе с JS-фрагментом, который будет обрабатывать перенаправление в браузере.

В моем случае, являясь бэкендом приложения Rails, вот какэта конечная точка выглядит так:

def redirect_me
  cookies[:s] = {value: 42, expires: 1.week, httponly: true}
  render body: "<html><head><script>window.location.replace('#{request.referer}');</script></head></html>", status: 200, content_type: 'text/html'
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...