session cookieをドメイン、サブドメイン間で共有する方法 rails (API only) + graphql + apollo


問題

domain.tld (frontend) -> login.domain.tld (backend)

  1. frontendとbackendがある
  2. backendはサブドメインだ(その反対、両方でも可)
  3. 両方のドメインでセッションクッキーを保持したい
  4. 認証はgraphql経由(ApolloClient使用)
  5. とりあえずbackendはRailsだ(API only)

解決

  1. corsの問題はクリアしておく
  2. サーバー側のcookie関係なくgraphqlで通信は可能な状態にしておく
  3. API onlyモードのRailsではcookieはデフォルトONではない
  4. セッションcookieももちろんONではない
  5. cookieをONにしてもデフォルトではサブドメインとcookietは共有しない

Rails側

cookieをONにする

application.rb
# dev、prod関係なく全体にcookieは必要だと思うのでここで設定

module App
  class Application < Rails::Application
    ...
    ...
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use ActionDispatch::Session::CookieStore,
                          domain: :all,
                          tld_length: 2,
                          secure: true,
                          same_site: :strict
  end
end

  • domain: :allでどんなサブドメインとも共有
  • tld_length: 自分のドメインがdomain.tld、サブドメインがa.domain.tldだったとして、tld_length: 2domain.tldになる。
    これがbase.domain.tldsub.base.domain.tldとかでbase.domain.tldを軸としてのサブドメイン共有であればtld_length: 3 = base.domain.tldとする。
  • secureとsame_siteは別の話

tld_length

a.b.c.domain.tld
  • tld_length: 2 => domain.tld ベース
  • tld_length: 3 => c.domain.tld ベース
  • tld_length: 4 => b.c.domain.tld ベース
application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::Cookies
end

これがないとcookieは動かない。

sessionをgraphqlで使う

graphql_controller.rb
class GraphqlController < ApplicationController
  def execute
    ...
    ...
    context = {
      session: session,
    }
end

context経由でsessionオブジェクトを渡しておいて、query、mutationでcontext[:session]経由で使う。

subscriptionをActionCable経由で使う場合

ActionCable内でsessionをとってくる事前の設定は全て済んでいるとして

graphql_channel.rb
class GraphqlChannel < ApplicationCable::Channel
  def execute(data)
    ...
    ...
    ...
    context = { channel: self, session: session }

ここでも同じようにcontext経由でsessionオブジェクトを渡す。

Apollo側の設定

You just need to pass the credentials option. e.g. credentials: 'same-origin' as shown below, if your backend server is the same domain or else credentials: 'include' if your backend is a different domain.

linkオブジェクト

  • credentials: include

これだけ

動作確認

domain.tldからbackend.domain.tldへのgraphql呼び出し

sessionを使うgraphqlを呼んだ時に、こういう風にcookieができていればOK。