我无法让 Safari 在使用 Fetch API(实际上是通过 fetch polyfill).相同的代码在 FF 和 Chrome 中都能正常工作(我使用原生和 polyfill fetch
进行了测试).
I am unable to get Safari to successfully apply Set-Cookie
from server responses when using the Fetch API (actually, via the fetch polyfill). The same code works correctly in FF and Chrome (I tested using both native and polyfill fetch
).
credentials: true
;Set-Cookie
标头进行响应;有人知道问题可能是什么吗?
Does someone know what the problem might be?
我已通读文档并查看了许多已关闭的错误报告.除非我错过了什么,否则我认为问题可能出在 '默认浏览器行为' 处理上使用 cookie 和 CORS——而不是使用 fetch(阅读 polyfill 源代码,似乎 100% 不了解 cookie).一些错误报告表明,格式错误的服务器响应可能会阻止保存 cookie.
I've read through the documentation and gone through many of the closed bug reports. Unless I missed something, I think maybe the problem is with the 'default browser behaviour' dealing with cookies and CORS -- and not with fetch (reading through the polyfill source code, it seems 100% ignorant of cookies). A few bug reports suggest a malformed server response can prevent cookies from being saved.
我的代码如下所示:
function buildFetch(url, init={}) {
let headers = Object.assign({}, init.headers || {}, {'Content-Type': 'application/json'});
let params = Object.assign({}, init, { credentials: 'include', headers });
return fetch(`${baseUrl}${url}`, params);
}
buildFetch('/remote/connect', {method: 'PUT', body: JSON.stringify({ code })})
.then(response => response.json())
.then(/* complete authentication */)
实际的授权请求如下.我正在使用 cURL 来获取准确的请求/响应数据,因为 Safari 很难复制/粘贴它.
The actual authorization request is below. I am using cURL to get the exact request/response data, since Safari makes it hard to copy/paste it.
curl 'https://mydevserver:8443/api/v1/remote/connect'
-v
-XPUT
-H 'Content-Type: application/json'
-H 'Referer: http://localhost:3002/'
-H 'Origin: http://localhost:3002'
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8'
--data-binary '{"token":"value"}'
* Trying 127.0.0.1...
* Connected to mydevserver (127.0.0.1) port 8443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
* Server certificate: mydevserver
> PUT /api/v1/remote/connect HTTP/1.1
> Host: mydevserver:8443
> Accept: */*
> Content-Type: application/json
> Referer: http://localhost:3002/
> Origin: http://localhost:3002
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8
> Content-Length: 15
>
* upload completely sent off: 15 out of 15 bytes
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: http://localhost:3002
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Api-Key, Device-Key
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Expose-Headers: Date
< Content-Type: application/json; charset=utf-8
< Content-Length: 37
< Set-Cookie: express:sess=[SESSIONKEY]=; path=/; expires=Fri, 17 Feb 2017 15:30:01 GMT; secure; httponly
< Set-Cookie: express:sess.sig=[SIGNATURE]; path=/; expires=Fri, 17 Feb 2017 15:30:01 GMT; secure; httponly
< Date: Fri, 17 Feb 2017 14:30:01 GMT
< Connection: keep-alive
<
* Connection #0 to host mydevserver left intact
{"some":"normal","response":"payload"}
回答我自己的问题.
虽然我理解他们的动机,但我发现这是 Safari 的按预期工作"行为,这让我非常愤怒.XHR(可能是原生获取时的原生获取)根本不支持第三方 cookie 的设置.这种失败是完全透明的,因为它是由脚本上下文之外的浏览器处理的,因此基于客户端的解决方案实际上是不可能的.
I find it pretty enraging that this is a "working as intended" behaviour of Safari, though I understand their motivation. XHR (and presumably native fetch when it lands natively) does not support the setting of third-party cookies at all. This failure is completely transparent because it is handled by the browser outside of the scripting context, so client-based solutions are not really going to be possible.
您将在此处找到的一个推荐解决方案是打开一个窗口或 iframe 到 API 服务器上的 HTML 页面并在那里设置一个 cookie.此时,第 3 方 cookie 将开始工作.这很丑陋,而且不能保证 Safari 不会在某个时候堵住这个漏洞.
One recommended solution you will find here is to open a window or iframe to an HTML page on the API server and set a cookie there. At this point, 3rd party cookies will begin to work. This is pretty fugly and there is no guarantee that Safari won't at some point close that loophole.
我的解决方案基本上是重新实现一个身份验证系统,该系统执行会话 cookie 的功能.即:
My solution is to basically reimplement an authentication system that does what session-cookies do. Namely:
X-Auth: [token]
,其中 [token]
是一个非常小的、短暂的 JWT,其中包含您所需的信息session(理想情况下只有用户 ID——在应用程序的生命周期内不太可能发生变化的东西——但如果权限可以在会话期间更改,则绝对不是权限之类的东西);X-Auth
添加到 Access-Control-Allow-Headers
;X-Token
响应标头,并在任何时候将其作为 X-Token
请求标头回显(您可以实现持久性通过使用本地存储——令牌过期,因此即使价值存在多年,也无法在某个时间点之后赎回);X-Auth: [token]
, where [token]
is a very small, short-lived JWT containing the information you require for your session (ideally only the user id -- something that is unlikely to mutate during the lifetime of your application -- but definitely not something like permissions if permissions can be changed during the session);X-Auth
to Access-Control-Allow-Headers
;X-Token
response header and echo it back as an X-Token
request header any time it sees it (you could achieve persistence by using local storage -- the token expires, so even if the value lives for years, it can't be redeemed after a certain point);请注意,JWT(或类似的东西)旨在解决一个完全不同的问题,并且由于重放"问题,真的不应该用于会话管理(想想如果用户打开两个窗口会发生什么情况)标头状态).然而,在这种情况下,它们提供了您通常需要的短暂性和安全性.最重要的是,您应该在支持它们的浏览器上使用 cookie,使会话信息尽可能小,让您的 JWT 尽可能短,并构建您的服务器应用程序以防意外和恶意重放攻击.
Note that JWT (or anything similar) is intended to solve a completely different problem and should really never be used for session management because of the "replay" problem (think what could happen if a user had two windows open with their own header-state). In this case, however, they offer the transience and security you normally need. Bottom line is you should use cookies on browsers that support them, keep the session information as tiny as possible, keep your JWT as short-lived as possible, and build your server app to expect both accidental and malicious replay attacks.
这篇关于Safari 不使用 JS Fetch API 设置 CORS cookie的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!