えむにわリソース

ITのスキマ的なあれこれを書きます。

Ruby on Rails 6.x + Omniauth でRedisのSessionを使う

f:id:m2wasabi:20200613110248p:plain

Ruby on Rails5.2以降、 Redisのキャッシュ機能を標準サポートされました。

Rails の OAuth2 クライアントとして Omniauthを使った場合、 無策で実装するとCSRFエラーが出ます。

OmniAuth::Strategies::OAuth2::CallbackError (csrf_detected | CSRF detected):

これはOmniauthが Railsとは別の Rackアプリケーションとして立ち上がっていて、Railsの設定を読まないためです。 公式サイトにはCookieセッションでの対応方法が書いてありますが、Redisで設定する場合の記述がなされていません。

github.com

手順

1. Redis stores for ActionPack のGemをインストールする

Rails上でRedisのSessionを使うのはもちろん、 ActionDispatch::Session::RedisStore を使うのに必要です。

Gemfile

gem 'redis-actionpack'

2. Rails の Session設定を書く

/config/initializers/session_store.rb

Rails.application.config.session_store :redis_store,
  servers: [ENV.fetch("REDIS_URL")],
  key: ENV.fetch("REDIS_SESSION_KEY"),
  expire_after: 1.month,
  threadsafe: true,
  signed: false,
  secure: Rails.env == 'production' ? true : false

3. Omniauth の Session設定を書く

Rackアプリケーションの設定でミドルウェアでSessionを読むようにします。

ここでRailsと同じ設定を書かなければSessionがつながりません。DRYじゃないので悔しいですが、同じ記述をします。

/config/application.rb

module App
  class Application < Rails::Application
    config.load_defaults 6.0

    config.session_store :redis_store,
                                           servers: [ENV.fetch("REDIS_URL")],
                                           key: ENV.fetch("REDIS_SESSION_KEY"),
                                           expire_after: 1.month,
                                           threadsafe: true,
                                           signed: false,
                                           secure: Rails.env == 'production' ? true : false

    config.middleware.use ActionDispatch::Cookies # Required for all session management
    config.middleware.use ActionDispatch::Session::RedisStore, config.session_options

    # ~以下略~
  end
end

これで動くはずです。

ハマリどころ

NoMethodError (undefined method private_id' for nil:NilClass): redis-rack (2.1.3) lib/rack/session/redis.rb:49:inblock (2 levels) in write_session'

上記のエラーが出る場合、Rackミドルウェアの読み込みで ActionDispatch::Session::RedisStore がオプションを正常に受け取っていない(or 間違っている)ところ疑うと良いです。 ここでめちゃくちゃハマりました。

    config.middleware.use ActionDispatch::Session::RedisStore, config.session_options

そういうわけで、やっていきましょう。