【Rails】APIモードかつクロスオリジン間でCookieやSessionを使う方法【SPA】

# 環境
- Ruby 3.1.2
- Rails 6.1.6
- Vite 2.9.9
- Vue 3.2.25
- Google Chrome (バージョン:103.0.5060.53)

# 前提
- RailsはAPIモードで使用
- フロントエンドとバックエンド間は異なるオリジンでの通信

# やり方
APIモードで起動したRailsはそのままではCookieを使うことができません。
なので、まずCookieを使うことができるようにします。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::Cookies # 追加
```

```ruby:config/application.rb
module App
  class Application < Rails::Application
    # ...
    config.middleware.use ActionDispatch::Cookies # 追加
    # セッションを使う場合は以下も追加しておく
    # config.middleware.use ActionDispatch::Session::CookieStore
  end
end
```
これでCookieを使うことができるようになるのですが、
フロントエンドとバックエンドでオリジンが異なる場合、
RailsでCookieをセットしてもブラウザに反映されないと思います。

よってさらに以下の設定を追加します。
ここでは、フロントエンドのオリジンが`http://localhost:3000`
バックエンドのオリジンが`http://localhost:8000`の開発環境を想定することにします。
## CORS設定に credentials: true を追加する
```ruby:config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:3000'

    resource '/api/v1/*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head],
      credentials: true # 追加
  end
end
```
`gem 'rack-cors'`を使用したCORS設定については下記の記事に説明があるので下記を参考にしてみてください。

https://qiita.com/ren0826jam/items/a96a713b25fef032f5c9

## フロントエンド側のaxiosの設定に withCredentials: true を追加する
```typescript:src/plugins/axios.ts
const axiosInstance: AxiosInstance = axios.create({
  baseURL: "http://localhost:8000/api/v1",
  headers: {
    "Content-type": "application/json",
  },
  withCredentials: true // 追加
})
```
※Fetch API等のaxios以外のHTTP通信を使用している場合は下記の記事を参考にしてみてください。

https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2


これで開発環境でCookieが使えるようになると思います。
ただし、本番環境ではさらにやるべきことがあります。

開発環境ではフロントエンドとバックエンドでポート番号のみの違いしかないですが、
本番環境ではフロントエンドのオリジンは`https://www.hogehoge.com`で
バックエンドのオリジンは`https://api.hogehoge.com`といったようなドメイン自体が異なるといった構成になると思います。
その場合、フロントエンドとバックエンドはクロスサイトという扱いになり、
CookieのSameSite属性をNoneにしなければなりません。

https://qiita.com/emacs_hhkb/items/ff6af4361b8a10f781a9

よってさらに以下の設定を追加します。

## CookieのSameSite属性をNoneにする設定を追加する
```ruby:config/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から新規追加された設定らしいので
Rails6.1未満の場合は下記の記事を参考に`gem 'rails_same_site_cookie'`を使って設定してみてください。

https://qiita.com/jnchito/items/1290a7b1dbf0ccf40c7b

SameSite属性をNoneにした場合は、CookieのSecure属性も付与しなければいけません。

## CookieのSecure属性を追加する。
cookieをセットしている箇所に`secure: true`を追加します。
```ruby
cookies[:hoge] = { value: 'piyo', http_only: true, secure: true }
```

## 本番環境をSSL化する
Secure属性が付加されたCookieはHTTPS通信でしか送信されないため
本番環境をSSL化してあげましょう。

これで本番環境でもCookieが使えるようになっていると思います。

## Secure属性付けたら開発環境もSSL化しないといけないの？
localhostの場合のみSecure属性のCookieでもHTTPで送信できるため、
開発環境をSSL化する必要はないみたいです。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie#sect4

とのことなので、開発環境・本番環境関わらずここまでの設定を一度にやっておくのが良いと思います。

# 参考
[【Rails】APIモードでcookieを使う](https://qiita.com/piggydev/items/663089428b70c481146b)
[クロスオリジンでcookieの挙動を確認する](https://qiita.com/Syy12345-Unity/items/eaa913ecc80e8cb96f0b)
[Railsガイド(ミドルウェアスタックの内部)](https://railsguides.jp/rails_on_rack.html#%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%81%AE%E5%86%85%E9%83%A8)