コードの修正
前提
deviseの対象となっているModelはUser
クラスとします。別なModelが対象になっている場合は適宜読み替えて下さい。
omniauth関連のgemを追加
下記のgemを追加します。
gem 'omniauth' gem 'omniauth-facebook' gem 'omniauth-twitter' gem 'omniauth-rails_csrf_protection' # こちらについては後述
Userにuid,providerフィールドを追加
Userにuid, providerを追加します。
uid, providerはSNSログイン時に各SNSから発行される値です。
class AddColumnsToUsers < ActiveRecord::Migration[5.2] def change add_column :users, :uid, :string add_column :users, :provider, :string end end
User Modelのdevise設定を変更
devise omniauthableを有効にする
Userのomniauthableを有効化します。
例
class User < ApplicationRecord # 変更前 devise :database_authenticatable, :registerable, :validatable, :recoverable # 変更後 devise :database_authenticatable, :registerable, :validatable, :recoverable, :omniauthable, omniauth_providers: %i[twitter facebook] # 略 end
ログインと登録処理を実装する
authから発行された providerとuidでUser
を検索します。
存在すればSNSログイン済なので検索ヒットしたユーザーを返し、存在しなければ新たに登録します。
def self.find_for_oauth(auth) user = User.where(provider: auth.provider, uid: auth.uid).first unless user user = User.create( name: auth.info.name, provider: auth.provider, uid: auth.uid, email: auth.info.email, password: Devise.friendly_token[0,20] ) end end
CallBack用のControllerを追加
omniauthを導入すると、各SNSで認証完了後にコールバックが呼ばれます。
コールバック内で前述のself.find_for_oauth
を呼びます。
正常に動作するとユーザーのインスタンスが返ってくるので、そのインスタンスを使ってログイン処理を行います。
例 ※クラス名は適宜自分のプロダクトに合わせて読み替えて下さい。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook callback_from :facebook end def twitter callback_from :twitter end private # コールバック処理を共通化しています def callback_from(provider) auth = request.env['omniauth.auth'] provider = provider.to_s @user = User.find_for_oauth(auth) if @user.persisted? sign_in_and_redirect @user, event: :authentication else session["devise.#{provider}_data"] = auth.except("extra") redirect_to new_user_session_path, alert: @user.errors.full_messages end end end
Viewを追加
例
※ pathメソッドの名称は各自のパスと異なる可能性があるので適宜読み替えて下さい。
omniauth-rails_csrf_protectionを入れているのでmethodをpostにする必要があります。
<%= link_to "Sign in with Facebook", user_facebook_omniauth_authorize_path, method: :post %> <%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
SNS側の設定
下記を参考にFacebookアプリを作成してAPIキーを取得します
Railsで Facebook ログインを実装してみた # 5. Facebook APIキーの取得
特にURLについては間違えないように設定していきます。
作成できたらアプリID、app secretを控えておきます。
同様にTwitterもアプリを作成します
Rails5 Twitterログインをdevise+omniauthで実装 # Twitter Developer登録
※ Twitterアプリの申請については審査が入ります。アプリの説明等を英語で記載する必要があります。また、審査が降りるまでの期間がどのくらいになるか不明です。
こちらも作成完了後、API KeyとAPI secret keyを控えておきます。
環境変数に設定
ローカルの場合
いくつか方法はありますが、今回はdotenvというgemを使いました。
gem 'dotenv-rails'
gem追加後、アプリ直下に .env
というファイルを作成します。
中身は下記のような形になります。
FACEBOOK_ID={Facebook アプリID} FACEBOOK_SECRET_KEY={Facabook app secret} TWITTER_API_KEY={Twitter API Key} TWITTER_SECRET_KEY={Twitter secret Key}
さらにリポジトリに追加しないようにgitignoreにも追加します。
.env
herokuの場合
実際に動作するサーバーはherokuを使用しているので、herokuの環境変数に設定します。
設定すべくき項目は.envの内容と同じです。
ハマったこと
Twitter APPにCallback URL設定漏れ
Twitter APPにはCallback URLを設定する箇所があります。これを正しく設定しないとTwitter認証が正しく動きません。
またローカルで開発している段階ではローカルホストアドレスも設定しておくのが良いと思います。
さらに注意点としては、Callback URLはググった記事などからコピペするのではなく、かならず自分のroutesを確認した上で設定することです。
記事と自分のプロダクトのパスが同じとは限らないからです。
Twitter 認証でemailが取得できない
Twitterアプリ作成後、デフォルトだとemailが取得しない設定になっているので設定を変更する必要があります。
CSRF問題
認証テスト時にCSRFの問題で認証できないことがあります。これについてはRails側でgemを追加すれば解消できます。
※ 本ページの omniauth関連のgemを追加, viewを追加 項目を参照下さい。
gem 'omniauth-rails_csrf_protection'
<%= link_to "Sign in with Facebook", user_facebook_omniauth_authorize_path, method: :post %> <%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
Cookieが大きすぎる
認証成功後、認証情報をcookieで保存するようにしていますがその内容が大きすぎてエラーになる事があります。
認証情報の内不要なものをcookieに保存しないようにします。
CallBack用のControllerを追加 のこの部分
session["devise.#{provider}_data"] = auth.except("extra")
auth
に含まれる認証情報の内、extra
という項目(その他の情報的なもので認証処理には不要)を除外してCookie保存しています。
以上です。