【Nuxt3×Rails API】クロスリジン間でCookieの保存・削除がハマったときの対処法

運営者:そうすけ

愛媛在住のエンジニア兼ブロガー。
工場勤務から社内SEにキャリアチェンジ。
主に社内向けのシステム開発を行っています。

【ブログ運営歴】2021.6~
【プログラミング歴】2022.3~

本業:Java,MySQL、個人:Javascript

認証などの情報をやり取りしたり、期限を管理したりするのに、よく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() メソッドに渡してください。

fetch("https://example.com", {
  credentials: "include",
});
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%A1

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のやり取りができるようになります。

かなりつまずきましたが、セキュリティの勉強になりました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

目次