Facebook認証機能の実装(2)
gemを追加
Facebook/Twitter認証に必要なgemを追加します。今回追加するgemは以下の通りです。
gem | 機能 |
---|---|
omniauth | omniauth本体 |
omniauth-facebook | omniauthのFacebook連携用ストラテジー |
omniauth-twitter | omniauthのTwitter連携用ストラテジー |
dotenv-rails | アプリIDやapp secretを環境変数で管理 |
なおdotenv-railsは、Railsアプリのルートディレクトリ直下に .env
ファイルを配置すると、 .env
ファイル内に記述されている環境変数を読み込んでくれる機能を提供するgemです。FacebookアプリのアプリIDやapp secretなどは、センシティブな情報なので設定ファイルに直接記述するより、環境変数としてGitの外で管理する方がよいです。
dotenv-railsは、Railsがdevelopment/testモード時にのみ有効となるようにインストールします。
…(中略)… gem 'devise' gem 'omniauth' gem 'omniauth-facebook' gem 'omniauth-twitter' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] # Adds support for Capybara system testing and selenium driver gem 'capybara', '~> 2.13' gem 'selenium-webdriver' gem 'dotenv-rails' end …(中略)…
以下のコマンドでgemをインストールします。なお、bundle installコマンドの「install」は以下のように「inst」と省略可能です。
bin/bundle inst
usersテーブルにカラムを追加
OmniAuthによるSNS連携では、サービス名を「provider」というカラム名で、サービスごとのユーザーIDを「uid」というカラム名で保存して管理します。また、SNS連携時に外部サービスで登録済みの名前(name)などを取得することもできます。今回は「name」というカラム名も合わせて追加します。
以下のコマンドでこれらのカラムを追加するマイグレーションファイルを作成します。
bin/rails g migration AddColumnsToUsers name:string provider:string uid:string
作成されたマイグレーションファイルを以下のコマンドで適用します。
bin/rails db:migrate
FacebookのアプリIDとapp secretを設定
deviseの設定ファイルにOmniAuth連携用の設定を記述する箇所があります。デフォルトではGitHub用の設定がコメントアウトされているので、その下に以下の通り設定を追加します。
…(中略)… # ==> OmniAuth # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET'] …(中略)…
次に、.envファイルにFacebookのアプリIDとapp secretを環境変数として指定します。
FACEBOOK_APP_ID=[アプリID] FACEBOOK_APP_SECRET=[app secret]
このように環境変数を設定することで、Railsアプリ側ではENV['環境変数名']
として.envに指定した値を利用することができます。
ルーティング/コントローラーの作成
deviseのコントローラーは、前回までの連載記事で紹介した通り、Rails enginesの仕組みを通してgemに組み込まれています。そのため、特にカスタマイズが不要であればgemに定義したものをそのまま流用することができます。
しかしOmniAuthとの連携を通じては、deviseが提供するコントローラーをカスタマイズする必要があります。具体的には、SNS認証後のコールバック時に外部サービスからアクセスされるエンドポイントに処理を実装するなどの対応が必要です。
deviseにはこういった場合に備えて、コントローラーを自動生成するコマンドが用意されているので、以下のコマンドを実行してdeviseが提供するコントローラーを作成します。
bin/rails g devise:controllers users
▼
create app/controllers/users/confirmations_controller.rb create app/controllers/users/passwords_controller.rb create app/controllers/users/registrations_controller.rb create app/controllers/users/sessions_controller.rb create app/controllers/users/unlocks_controller.rb create app/controllers/users/omniauth_callbacks_controller.rb =============================================================================== Some setup you must do manually if you haven't yet: Ensure you have overridden routes for generated controllers in your routes.rb. For example: Rails.application.routes.draw do devise_for :users, controllers: { sessions: 'users/sessions' } end ===============================================================================
最後の引数のusers
は、認証に使うユーザーテーブルの物理名を指定します。今回は、前回記事から継続してusersテーブルを使用していますのでusersを指定します。実行結果の通り、deviseが提供するコントローラーがapp/controllers/users
配下に自動生成されます。
実行結果に示されるように、作成したコントローラーが使われるようにルーティングを再定義します(リスト4)。
Rails.application.routes.draw do devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: 'users/registrations' } root to: 'home#index' end
このように定義することで、deviseでは呼び出されるコントローラーを指定することができます。今回の機能実現のためにはSNS認証時のコールバック処理(omniauth_callbacks)とユーザー登録/編集処理(registrations)の実装をカスタマイズするため、これらのコントローラーの定義を追加しています。
次に、作成されたコントローラーのうち、SNS認証時のコールバック処理を定義するomniauth_callbacks_controller.rbを以下のように修正します。
class Users::OmniAuthCallbacksController < Devise::OmniAuthCallbacksController # You should configure your model like this: # devise :omniauthable, omniauth_providers: [:twitter] # You should also create an action method in this controller like this: def twitter callback end def facebook callback end …(中略)… private def callback @user = User.find_or_create_for_oauth(request.env['omniauth.auth']) if @user.persisted? sign_in_and_redirect @user else session['devise.user_attributes'] = @user.attributes redirect_to new_user_registration_url end end end
deviseが提供するジェネレーターコマンドで作成したコントローラーは、継承元がDevise::OmniAuthCallbacksController
となっていることに注目してください。deviseが提供するコントローラーを継承しているので、あらかじめdeviseによって用意されたメソッドがコメントアウトされた状態となっており、修正が必要な部分のコメントアウトを外し、処理を追加してカスタマイズしていきます。なお、今回のFacebook/Twitter連携では処理を共通化できるので、privateメソッドであるcallbackメソッドに共通化した処理を定義して呼び出しています。
callbackメソッドの1行目にあるUserモデルのクラスメソッドfind_or_create_for_oauthは、OAuth認証の情報から取得されるユーザーが登録済みであればfind結果を、未登録であれば新規作成したユーザーのインスタンスを返すメソッドです。具体的な処理はこの後Userモデルに実装していきます。ここでは、request.env['omniauth.auth']
とすることで、OAuth認証後にユーザー情報が得られることを理解してください。
後続のif文において、ユーザー情報が正常に保存できた(persisted?メソッドがtrueを返す)場合は、該当ユーザーでログインしてユーザー情報の画面にリダイレクトします。ユーザー情報が正常に保存できなかった場合は、セッションにユーザー情報を代入しつつユーザー登録画面にリダイレクトします。