概要
構成はNuxt.jsとRailsのAPIモード。
ローカルでは問題なかったのですが、
STGと本番環境にあげた際にクロスオリジン間でCookiesの保存ができなかった経験があり、
色々と設定に苦しんだので備忘録としてまとめます。
この記事で話すこと
- クロスオリジン間でのCookieを共有するための設定方法
この記事で話さないこと
- クロスオリジンとは何か
- gem 'rack-cors'によるCORSの設定
本題
実現したかったこと
ActionCable使って特定のユーザーに配信を行いたく、
以下のようにユーザーの識別のためにランダムな文字列を発行しcookieに保存したかった。
# 例 ※実際の実装内容とは異なります
def set_code
# cookieを見て識別子が保存されていなければ発行
cookies[:user_code] ||= SecureRandom.hex
end
が、毎回保存できておらず識別子が生成されてしまっていた。
解決方法
バックエンドとフロントエンドの両方で諸々の設定をしました。
まずはバックエンドのRailsから。
Cookieを使うための準備
RailsのAPIモードの場合、このままではcookieが使えないので設定をする必要があります。
class ApplicationController < ActionController::API
include ActionController::Cookies # これを追加する
次に、RailsのAPIモードではCookieを処理するミドルウェアがないため、
application.rbに次の設定を追加します。
module App
class Application < Rails::Application
# ・・・
config.middleware.use ActionDispatch::Cookies
config.action_dispatch.cookies_same_site_protection = :none
# ・・・
end
end
※cookies_same_site_protectionはRails6.1で追加された設定のようです。
参考:https://qiita.com/SoarTec-lab/items/10280dbea1195bf21b3c
CORSの設定
現状の設定ではフロントエンドとバックエンドのオリジンが異なる場合に
ブラウザに保存ができないので、次はCORSの設定をします。
許可したいオリジンを指定するのに加え、
credentials: true
の設定が必要となります。
これでクロスオリジン間でcookieが使用できます。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "http://localhost:8080" # ここに許可するオリジンの指定。正規表現も使えるので、可能であればそれで指定する方がベターだと思います。
resource "*",
headers: :any,
credentials: true, # これが必要!!!
methods: [:get, :post, :options, :delete, :put, :patch]
end
end
CookieのSecure属性を追加
SameSite属性がNoneの場合、
Secure属性も付与しなければいけないため次のようにします。
(※開発環境ではなくても動きました)
def set_code
# cookieを見て識別子が保存されていなければ発行
- cookies[:user_code] ||= SecureRandom.hex
+ cookies[:user_code] ||= {value: SecureRandom.hex, http_only: true, secure: true}
end
Nuxt.jsの設定
フロントエンドはNuxt.jsを使用していたため、
次のように設定を追加しました。
credentials: true
を追加することで、リクエストヘッダーのwithCredentials
をtrueにします。
参考:https://axios.nuxtjs.org/options/#credentials
ドメインにcookieを設定する際にこちらが必要となるようです。
異なるドメインからの XMLHttpRequest のレスポンスは、リクエストを行う前に withCredentials を true に設定しない限り、自身のドメインの Cookie 値を設定することができません。
https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/withCredentials
// ・・・
axios: {
// ・・・
credentials: true
},
// ・・・
最後に
これだけ諸々の設定をしてようやく望み通りの動作が実現できました。
CORSやCookieのいい勉強になりました。
もし間違っている点や、
もっとこうした方がいいよなどあればコメント頂けますと幸いです。
参考