これは何
Flutter Web の画面開発で、ローカルで動作しているフロント画面から 共有の開発環境で動作している BFF(Backend for frontend) サーバへのCookie送出が Cross Domain 制約に引っかかってしまう件の解決策です
Flutter Web での開発で発生した話ですが、本記事の状況は React 含め、Web系のFrontend開発全般で共通する話です。
元々はアクセストークンを localStorage に保管する方式で開発していたのですが、昨今のlocalStorageへのトークン保管是非の議論をうけ、多少ともリスクが減りそうなので Cookie 方式にしよう、というチーム議論の上で Cookie 利用に切り替えた所、見事にハマってしまいました。
結論
- CORSでのCookie送出に必要な設定をした上で、
- Cookieの属性を
SameSite=None
、Secure
とする
(商用環境では SameSite=Lax
。開発環境のみこのように設定)
何が問題だったのか
ローカルで Frontend が動作し (Flutter Web のローカルサーバ)、BFF は 共有開発環境でドメイン付きで動作しています。
Frontend(Flutter Web) | BFF Server |
---|---|
http://localhost:3000 | https://hoge-api.dev.example.com |
この構成では Frontend から BFF に Cookie が飛ばないことが問題でした。ブラウザは Chrome です
フロントエンドを共有開発環境に反映した場合は以下の構成で動作することになり、FrontendとBFFがCookie的には同一ドメインなので特に問題はありませんでした。
Frontend(Flutter Web) | BFF Server |
---|---|
https://hoge.dev.example.com | https://hoge-api.dev.example.com |
また、BFFをローカルに立てた場合も CORS ではあるが Cookie としては同一ドメインなので問題なし。ただし Secure フラグは立てられないので、環境を判断してサーバで出し分ける必要があります。
Frontend(Flutter Web) | BFF Server |
---|---|
http://localhost:3000 | http://localhost:8082 |
CORSでのCookie送出に必要な設定
サーバ側
- 適切なCORSヘッダを返す
-
access-control-allow-credentials: true
を返す -
access-control-allow-origin
には*
ではなく具体的なサーバのURLを記載- この場合は
http://localhost:3000
- この場合は
-
access-control-allow-headers
にも*
ではなく具体的なヘッダを記載- ただし
cookie
は書かなくても良いようです
- ただし
-
- サーバがセットするCookie には
SameSite=None
、Secure
を指定
クライアント側
- fetch API の場合は
credentials: 'include'
をセット
fetch(url, {credentials: 'include'})
ハマったポイント
いろいろと不勉強な点があり、解決に時間がかかってしまいました。
- CORSでいうクロスサイトとCookieのクロスサイトの定義が違う認識がなかった
- CORS元が http で CORS先が https の場合、cookie に Secure をセットすべきかどうか分からなかった
-
SameSite=None
指定は避けたほうが良いかなというなんとなくの認識
※本件、セキュリティ方面に詳しい方からみると不適切な内容かもしれず、指摘があればぜひお聞きしたいです
その他ちょっとした整理
2つのURLの違い | fetch API/XHR の CORS 対象かどうか | Cookie が共有されるかどうか |
---|---|---|
ポートが違う | CORS | 共有される |
プロトコルが違う(http/https) | CORS | 共有される、ただし Secure属性があると https のみ |
同一ドメインでホスト名が違う・サブドメイン | CORS | Domain指定に合致していれば共有される |