Fetch APIを使用している場合(実際には fetch polyfill を使用)、Safariでサーバー応答からSet-Cookie
を正常に適用できません。同じコードがFFとChrome(私はネイティブとポリフィルの両方を使用してテストしたfetch
)で正しく機能します)。
credentials: true
を設定しています。Set-Cookie
ヘッダーで応答します。誰かが問題が何であるか知っていますか?
私はドキュメントを読み、多くの クローズされたバグレポート を調べました。私が何かを見逃していない限り、おそらく問題は 'default browser behaviour' でcookieとCORSを扱うことであり、fetchではありません(ポリフィルのソースコードを読むと、100%無知のようです)クッキー)。いくつかのバグレポートは、不正なサーバー応答がCookieの保存を妨げることを示唆しています。
私のコードは次のようになります:
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 */)
実際の承認リクエストは以下のとおりです。 Safariではコピー/貼り付けが難しいため、正確な要求/応答データを取得するためにcURLを使用しています。
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の設定をまったくサポートしていません。この失敗は、スクリプトコンテキスト外のブラウザーによって処理されるため、完全に透過的であり、クライアントベースのソリューションは実際には不可能です。
ここで推奨される解決策の1つは、APIサーバーでHTMLページにウィンドウまたはiframeを開き、そこにCookieを設定することです。この時点で、サードパーティのCookieが機能し始めます。これはかなりあいまいであり、Safariがその抜け穴を閉じることがないという保証はありません。
私の解決策は、基本的に、セッションCookieが行うことを行う認証システムを再実装することです。つまり:
X-Auth: [token]
を追加します。ここで、[token]
は、セッションに必要な情報(理想的にはユーザーIDのみ)を含む非常に短い、存続期間の短いJWTです。アプリケーションの存続期間-ただし、セッション中にアクセス許可を変更できる場合は、アクセス許可のようなものではありません);X-Auth
をAccess-Control-Allow-Headers
に追加します。X-Token
応答ヘッダーを探し、それが見つかるたびにX-Token
要求ヘッダーとしてエコーバックします(ローカルストレージを使用して永続性を実現できます-トークンが期限切れになるため、価値は何年もの間存続し、特定のポイントの後は償還できません);JWT(または類似のもの)は完全に異なる問題を解決することを目的としており、「リプレイ」問題のためにセッション管理に実際に使用すべきではないことに注意してください(ユーザーが独自のヘッダー状態で2つのウィンドウを開いているとどうなるかを考えてください) )。ただし、この場合は、通常必要な一時性とセキュリティが提供されます。結論としては、それらをサポートするブラウザーでCookieを使用し、セッション情報をできるだけ小さくし、JWTをできるだけ短期間で維持し、サーバーアプリをビルドして、偶発的および悪意のあるリプレイ攻撃の両方を予期する必要があります。
参考までに、これを約18か月後に試したところ、このソリューションはうまくいきませんでした。または、断続的に、一部のユーザーにとっては非常に奇妙なようでした。
ここで推奨される解決策の1つは、APIサーバーでHTMLページにウィンドウまたはiframeを開き、そこにCookieを設定することです。この時点で、サードパーティのCookieが機能し始めます。これはかなりあいまいであり、Safariがその抜け穴を閉じることがないという保証はありません。
推測としては、Safariが内部で使用しているロジックは、Cookieを取得するために管理した順序、またはより複雑で不透明なものに依存すると考えられます。
React APIとは異なるホストからアプリを提供しているため、または両方のサイトを制御しているため、これが発生した場合のオプションとなる可能性があります。 DNSを使用するには:
クライアントはwww.company-name.comから提供され、APIはcompany-name.herokuapp.comにありました。 [〜#〜] cname [〜#〜]レコードapi。company-name.com-> company-name.herokuapp.comを作成し、それを使用するクライアントからAPIへのリクエストに対して同じドメインのサブドメインであるSafariは、それを「サードパーティ」のCookieと見なすのをやめました。
良い点は、コードがほとんど含まれておらず、確立されたものをすべて使用していることです...不利な点は、httpsを使用する場合、APIホストを制御または所有する必要があることです。有効な証明書が必要です。クライアントドメインの場合、またはユーザーに証明書の警告が表示されます。そのため、問題のAPIが自分のものでもパートナーのものでもない場合、これは機能しません(少なくともエンドユーザー向けのものでは機能しません)。