RailsアプリにOAuth2認証を実装する(Facebook編)

さて前回に引き続いて今度はFacebookからのOAuth2認証。

gem 'omniauth-facebook'

Gemfileにomniauth-facebookを追加して実装開始。

なお第一段階としてGoogle+での認証処理をそのまま横展開することで実装してみた。すなわちほとんどコピペレベルだが、動作の完遂を優先するためリファクタリングは次の段階でやる*1。一旦は目を瞑っていてくれたまえワトソン君。

app/controllers/users/omniauth_callbacks_controller.rbには下記のメソッドを追加。

  def facebook
    @user = User.find_for_facebook(request.env['omniauth.auth'])

    if @user.persisted?
      flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Facebook'
      sign_in_and_redirect @user, event: :authentication
    else
      session['devise.facebook_data'] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end

続いて app/models/user.rb は下記のように変更。

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :confirmable, :lockable, :timeoutable, :omniauthable,
         :omniauth_providers => [:facebook, :google] # ←この行追加

omniauth_providersをキーに設定して:facebookと:googleを配列にしたものを値におく。

さらに user.rb のメソッドに find_for_facebook を追加。

  def self.find_for_facebook(auth)
    user = User.find_by(email: auth.info.email)

    unless user
      user = User.create(email:    auth.info.email,
                         provider: auth.provider,
                         uid:      auth.uid,
                         token:    auth.credentials.token,
                         password: Devise.friendly_token[0, 20])
    end
    user
  end

そして config/initializers/devise.rb に下記を追加。

  config.omniauth :facebook,
                  ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'],
                  name: :facebook, scope: %w(email)

これでOAuth2認証は動くようになる。ビューにはGoogle+の時と同じように下記のリンクを入れておく。

<%= link_to 'Sign in with Facebook', user_facebook_omniauth_authorize_path %>

コードの上ではこれで充分だが、肝心のFacebookでのAPPIDとシークレットの取得がちょい面倒。Google+にしろFacebookにしろ開発者向けサイトのUIが頻繁に変わるので過去の情報もかなりの確率でアテにならなかったりする。2017年8月時点でこうだったよというのを次のエントリあたりでまとめる予定なのでお楽しみに。

*1:逆に言えば認証ロジック自体はどこ相手でも違いがないということ。