運営者:そうすけ
愛媛出身の30代のブロガー兼ソフトウェアエンジニア。
フロントエンド・API開発を行っています。
ITエンジニアとしての暮らしやキャリアなどの発信をしています。
趣味はガジェットと植物。
認証などの情報をやり取りしたり、期限を管理したりするのに、よくCookieにトークンを載せるという方法が使用されます。
動かして身につく。RailsAPIとNuxt.jsで作るJWT(JSONWebToken)ログイン認証【22時間超解説】の講義はNuxt2のやり方でしたが、参考になりました。
nuxt3とRailsでcookie情報にトークンによるログイン処理を行ったアプリを実装したかったのですが、かなりハマりました。
クロスオリジン間の認証処理、めちゃくちゃ複雑でした。
WEB初心者だと絶対詰まると思うので、備忘録も含めて解説します
やりたかったこと
- ブラウザ(Nuxt)側から認証情報を入力し、Rails側にPOSTリクエストする
- Rails側で認可された場合、レスポンスヘッダにCookie情報を付与する
- 受け取ったブラウザは、Cookie情報をCookieに保管する。
やりたいことはこの3点なのですが、Cookieの認証情報をRails側からブラウザへ送付する際非常に苦労しました。。
環境
Rails7.0 APIモード
Nuxt3 3.10
Docker
Nuxt3とRailsでCookie送受信をする準備
まずはクロスオリジンでAPI通信ができるようにしよう
まず、異なるオリジン間でAPI通信ができることと、Cookieのやり取りができることに問題を分けます。
通常異なるオリジン間(NuxtサーバーとRailsサーバー)では設定を行わないと、Railsから情報が取得できません。
クロスオリジン間でAPI通信できることがまず第一歩の壁のため、API通信を行っていない方はまずAPI通信ができることを確認してください。
参考記事書いているので、よかったらみてみてください。
Nuxt3とRailsでCookie送受信をするのに必要なこと
- rails側にCookie処理を行うミドルウェアを入れる
- リクエストに『Credential』についてのオプションを付与する
- レスポンスに『Credential』についてのオプションを付与する
- CookieのSameSite属性をnoneにする
- Rails側でCookie付与するときのSecure属性をTrueにする
合計5つの処理を意図的に行って、やっと初めてCookie操作がクロスオリジン間で可能になります。
rails側にCookieのミドルウェアモジュールを追加
まず、RailsのAPIモードはデフォルトでCookie操作をするミドルウェアが存在しません。
なので、config/application.rbにconfig.middleware.use ActionDispatch::Cookies と記述します
require_relative "boot"
require "rails/all"
module App
class Application < Rails::Application
# 追加
#Cookieを処理するミドルウェア
config.middleware.use ActionDispatch::Cookies
config.api_only = true
end
end
Cookieを扱うミドルウェアを使用できるように設定します。
詳しい内容はドキュメントを参考にしてみて下さい。
Nuxtのリクエストにcredentials:includeを追加する
Nuxt側のリクエストのFetch関数にオプションにwithCredentials: trueを記載します。
Nuxt3でしたら、UseFetchか$fetchを使用するときのオプションに記載してあげます。
このオプションはクッキーの送受信をするかどうかの設定です。
これで、レスポンス時にRails側でクッキーに加えることを許可してくれます。
こんな感じで、オプションに付け加えましょう。
const req = await $fetch('RailsのURL',{
method:"POST",
credentials:'includes',
body:{
email:'souske@souske.com',
password:'password'
}
})
Nuxt側
細かい話だと、リクエスト時にcredentials: 'include'
を加えるとレスポンスヘッダにAccess-Control-Allow-Credentials:がつくようになります。
ブラウザーに資格情報のついたリクエストを送るようにするには、同一オリジンの場合もオリジン間の呼び出しの場合も、
credentials: 'include'
をinit
オブジェクトに追加してfetch()
メソッドに渡してください。https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch#%E8%B3%87%E6%A0%BC%E6%83%85%E5%A0%B1%E3%81%A4%E3%81%8D%E3%81%AE%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%AE%E9%80%81%E4%BF%A1fetch("https://example.com", { credentials: "include", });
Railsのレスポンスにcredentials: trueを記載する
Rails側でのレスポンスにcredentials: trueを記載します。
クロスオリジン設定時に、config\initializers\cors.rbを作成します。
そのファイルの中のresourceの下に、credentiials:trueを記載してあげます。
#api\config\initializers\cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ENV["API_DOMAIN"] || ""
resource "*",
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
詳しく言うと、レスポンスヘッダーにAccess-Control-Allow-Credentials: trueを明示的につけてやることで、CookieにRails側からアクセスして、保存・送信ができるようになります。
CookieのSameSite属性をNoneにする
CookieのSameSite属性をNoneにします
RailsのミドルウェアにSameSite属性の設定を行います。
こうすることでクロスオリジン時にクッキーのアクセスを許可することができます。
#config/application.rb
# 追加
#Cookieのクロスオリジン時にクッキーのアクセスを許可する
config.action_dispatch.cookies_same_site_protection = :none
ブラウザのCookieには属性が存在します。
その中の一つでSameSite属性が存在し、ドメイン間のCookieのやり取りを定義する属性があります。
3つ属性値が存在します。
属性 | 内容 |
---|---|
None | ドメインをまたいでCookieの受け渡しが可能 |
Lax | (GETでのリクエストのみ)ドメインをまたいでCookieの受け渡しが可能 |
Strict | ドメインをまたいでCookieの受け渡しが不可 |
chromeなどモダンブラウザではデフォルトで【Lax】になっており、クロスオリジン間でCookieのやり取りがセキュリティ上できないようになっています。
つまり、SameSite属性がNoneのときしかCookieの操作ができないです。
Secure属性の設定を行う
CookieのSameSite属性がNoneの時は、Secure属性をtrueにないとCookieの操作ができません。
Cookieにセットするcookies関数をRailsで使用するときにsecure属性をTrueにしましょう。
# refresh_tokenをcookieにセットする
def set_refresh_token_to_cookie
cookies[session_key] = {
value: refresh_token,
expires: refresh_token_expiration,
secure: true,
http_only: true
}
end
Googleによると、クロスオリジン間でCookieのやり取りできるようにセキュリティ緩めたんだから、せめてHTTPSで値を送受信しなさい。ということらしいです。
ちなみに、ローカル環境での開発はHTTPではないことがほとんどだから、開発時はやりとりできないの?と思った方もいるかもしれませんが、localhostだとできるみたいです。
まとめ
- rails側にCookie処理を行うミドルウェアを入れる
- リクエストに『Credential』についてのオプションを付与する
- レスポンスに『Credential』についてのオプションを付与する
- CookieのSameSite属性をnoneにする
- Rails側でCookie付与するときのSecure属性をTrueにする
ここまですることでやっとクロスオリジン間でCookieのやり取りができるようになります。
コメント