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

![UkkfkNYM.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16757/2e258126-70a3-c449-edcb-0bf69d6c6651.jpeg)


# 問題

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


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

# 解決

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

## Rails側

### cookieをONにする

```rb: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
```

https://api.rubyonrails.org/v6.1.3.2/classes/ActionDispatch/Cookies.html

- domain: `:all`でどんなサブドメインとも共有
- tld_length: 自分のドメインが`domain.tld`、サブドメインが`a.domain.tld`だったとして、`tld_length: 2`は`domain.tld`になる。  
これが`base.domain.tld`、`sub.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 ベース

```rb:application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::Cookies
end
```

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

### sessionをgraphqlで使う

```rb:graphql_controller.rb
class GraphqlController < ApplicationController
  def execute
    ...
    ...
    context = {
      session: session,
    }
end
```
context経由でsessionオブジェクトを渡しておいて、query、mutationで`context[:session]`経由で使う。

#### subscriptionをActionCable経由で使う場合

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

https://www.google.com/search?q=rails6+actioncable+session&biw=1466&bih=832&tbs=qdr%3Ay&ei=xkWkYM_eG4uUr7wP-bmBgAw&oq=rails6+actioncable+session&gs_lcp=Cgdnd3Mtd2l6EAMyBQgAEM0COgcIABCwAxAeUKwPWKwPYKYTaAFwAHgAgAFwiAHaAZIBAzAuMpgBAKABAaoBB2d3cy13aXrIAQHAAQE&sclient=gws-wiz&ved=0ahUKEwjP2b3NqdTwAhULyosBHflcAMAQ4dUDCA4&uact=5

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

```

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



## Apollo側の設定

https://www.apollographql.com/docs/react/networking/authentication/#cookie

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch

> 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呼び出し

![https___subsystem_mgbf-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16757/cfcd3123-52b7-5f36-f31f-ca8093c80f7a.png)

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