Reactjs 前端/后端分离:Safari不存储API中的cookie,该API托管在与其前端SPA客户端不同的域中

Reactjs 前端/后端分离:Safari不存储API中的cookie,该API托管在与其前端SPA客户端不同的域中,reactjs,rest,authentication,cookies,safari,Reactjs,Rest,Authentication,Cookies,Safari,据我所知,我有一个现在相当普遍的设置:一个位于自己域中的后端RESTAPI,比如说myapi.com,以及一个在其他地方提供服务的单页前端应用程序,比如myapp.com SPA是API的客户端,API要求用户在进行操作之前进行身份验证 后端API使用cookies存储某些允许来源的会话数据,其中myapp.com。这是为了有一个安全的总线来传输和存储身份验证数据,而不必担心客户端 在Chrome、Opera和Firefox中,这样做很好:进行API调用以验证用户身份,返回cookie并将其存储

据我所知,我有一个现在相当普遍的设置:一个位于自己域中的后端RESTAPI,比如说
myapi.com
,以及一个在其他地方提供服务的单页前端应用程序,比如
myapp.com

SPA是API的客户端,API要求用户在进行操作之前进行身份验证

后端API使用cookies存储某些允许来源的会话数据,其中
myapp.com
。这是为了有一个安全的总线来传输和存储身份验证数据,而不必担心客户端

在Chrome、Opera和Firefox中,这样做很好:进行API调用以验证用户身份,返回cookie并将其存储在浏览器中,以便与下一次调用一起推送

另一方面,Safari确实收到饼干,但拒绝储存:

我怀疑Safari将API域视为第三方cookie域,因此阻止存储cookie


这是狩猎中预期的行为吗?如果是这样的话,有哪些最佳实践可以绕过它?

在这一点上延续传统

TL;DR.这是狩猎中的理想行为。绕开它的唯一方法是将用户带到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
更新:上述解决方案可以很好地让用户登录,即正确地“白名单”当前浏览器会话的API域

但不幸的是,用户刷新页面会使浏览器重置为原始状态,API域的第三方cookie被阻止

我发现处理窗口刷新的一个好方法是在页面加载时将用户重定向到一个API端点,该端点与上面的端点相同,然后将用户重定向到他们导航到的原始URL(正在刷新的页面):

更复杂的是,如果响应的HTTP状态为30X(重定向),Safari将不会存储cookie。因此,Safari友好的解决方案包括设置cookie并返回一个200响应以及一个JS代码段,该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
def重定向\u me
cookies[:s]={value:42,expires:1.week,httponly:true}
呈现主体:“window.location.replace(“#{request.referer}”);”,状态:200,内容类型:“text/html”
结束

不仅适用于Safari(默认情况下会阻止第三方cookie)。Chrome还具有此设置,启用此设置后,将阻止所有第三方Cookie。如果将sameSite标志与secure和httponly标志(例如:{httponly:true,secure:true,sameSite:'none')一起使用,Chrome在@Ioanna下运行良好。然而,你发布的这个解决方案似乎很痛苦,椰子?在这种情况下,这是让safari储存饼干的唯一方法,这似乎很疯狂?我本以为cors来源标志和cookie域标志会给cookie合法性提供足够的保证,但我已经尝试过了,没有运气。我不知道这是一个SameSite/安全配置的问题,还是这个技巧已经不可能了。这在2021年对你仍然有效吗?嗨,我在使用express session时遇到了这个问题。您是否有机会获得使用javascript/express的公认解决方案?
if(performance.navigation.type == 1 && /^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
  window.location.replace(`http://myapi.com/redirect_me`);
}
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