Devise + omniauthでfacebookでログインする機能を実装でハマったところを説明
高専事変から1週間以上がたち、久しぶりの更新となります。
- 高専事変とは -
高専ってすごいというタイトルでカリキュラムをさらしたら、PV数がめちゃくちゃのびたという事変
今回は普通にRailsのtipsを。
- 前提条件
- 参考資料
- ハマった原因
- まとめ
前提条件
まず状況としてはこういう状態でした。
- Deviseでログイン機能を実装
- しばらくはDeviseオンリー
- 途中からFacebookログインを追加
という感じ。
同じ条件の人がいれば参考に。
またvalidationを厳しくもうけており、パスワード確認が一致しないとダメなど。
今回これがハマった原因でした。
参考資料
Devise + OmniAuth + OmniAuth Facebookで Facebook 認証 - present
Ruby - deviseでfacebook,twitter認証 - Qiita [キータ]
参考資料には上記2点でした。どちらもにたようなことが書かれておりますので、通常であればどちらかを参考にするとうまくいくでしょう。
ハマった原因
データベースに保存する際、バリデーションエラーが起きていたことが原因です。
なので
- 最初のコード
- デバッグ(エラーの原因を探る)
- 解決したコード
という流れで説明します。
最初のコード
こちらは先に出した参考資料のものをそのまま利用しました。
中でも今回重要な部分だけをpickupします。
model/user.rb内
def self.find_for_facebook_oauth(auth, signed_in_resource=nil) user = User.where(:provider => auth.provider, :uid => auth.uid).first unless user user = User.create({ :name => auth.extra.raw_info.name, :provider => auth.provider, :uid => auth.uid, :email => auth.info.email, :access_token => auth.credentials.token, :password => Devise.friendly_token[0, 20], }) end user end
こんな感じ。(参考資料そのまま)
処理としては、
このようなメソッドです。
問題となったのは何回やってもレコードがuserテーブルにインサートされないという現象でした。
どこに問題があるかを判断するために次のようなコードを書きました。
def self.find_for_facebook_oauth(auth, signed_in_resource=nil) user = User.where(:provider => auth.provider, :uid => auth.uid).first unless user user = User.new({ :name => auth.extra.raw_info.name, :provider => auth.provider, :uid => auth.uid, :email => auth.info.email, :access_token => auth.credentials.token, :password => Devise.friendly_token[0, 20], }) if user.save p "success" else p "failed" p user.errors end end user end
このように変更しました。
変更点はcreateメソッドではなく new + saveメソッドを使うというところ。
createメソッドはnewとsaveを同時に行うというメソッドです。
それを分けることでどこでエラーが出ているかがわかります。
この書き方であれば、エラーの内容も同時に出力されます。
このとき、出力した内容はバリデーションが通ってない時に出たエラーでした。
(具体的にはパスワードが確認用と一致していないなど)
なのでfacebookログインの場合バリデーションを無視させなければいけない。
そこで解決用に次の内容に変更しました。
def self.find_for_facebook_oauth(auth, signed_in_resource=nil) user = User.where(:provider => auth.provider, :uid => auth.uid).first unless user user = User.new({ :name => auth.extra.raw_info.name, :provider => auth.provider, :uid => auth.uid, :email => auth.info.email, :access_token => auth.credentials.token, :password => Devise.friendly_token[0, 20], }) user.save(:validate => false) end user end
このようにsaveのオプションとして :validate => false
という記述にしました。
これでバリデーションは無視され、レコードが登録されました。
まとめ
ただこの方法はなんか本質でない気がするので、
登録段階でバリデーションを通るようなコードを書くことをお勧めします。
同様の問題に関する資料がなかったので今回書きました。