LoginSignup
14
13

More than 5 years have passed since last update.

Facebookログイン認証機能を実装してみた + エラーの原因はバリデーションだった[Rails]

Last updated at Posted at 2018-12-08

最初に

webサービスを開発するにあたってユーザー認証機能はマストですよね。
いかにUXを良くしユーザに使ってもらえるサービスにするかは全ての開発者の望むところだと思います。
sns認証サービスに対応しておくとユーザの登録の手間が省け、ユーザ数の増加に繋がるはずです。
そこで、今回はfacebook認証を実装し、「サクッとログイン」を叶えていきましょう。

devise認証機能を実装済みという前提でお話していきますので、まだの方は下記記事を参考にdeviseによるログイン機能を実装しておきましょう。

[Rails] deviseの使い方(rails4版)

1. 「uidと」「provider」をカラムに追加

Userテーブルに、uid(任意のid)、provider(facebookやtwitter)の2つのカラムを追加します。

$ rails g migration AddColumnsToUsers uid:string provider:string
$ rake db:migrate

2. Facebookアプリを作成

Facebook Developerにアクセスして、「新しいアプリを追加」を選択。

Screen Shot 2018-10-31 at 8.21.20.png

続いて、表示されたモーダルウィンドウにメールアドレスとアプリ名(適当で良い)を記入。

Screen Shot 2018-10-31 at 8.25.30.png

左メニューの「設定」→「ベーシック」を選択。

遷移先ページの一番下の「+プラットホームを追加」を選択。
そこから、「Website」を選び、url記入欄「サイトURL」に下記のように記述する。

p2me_―_設定_-_開発者向けFacebook.png

注:production環境の場合は適当なURLに変更する。

これでFacebook側の設定は終了です。

3. Gemを追加

gem 'omniauth'
gem 'omniauth-facebook'

の2行をGemfileに追記する。

そして、

$ bundle install

4. AppIDとSecretIDを追加

config/initializers/devise.rbに下記のコードを追記する。。

devise.rb
Devise.setup do |config|
  config.omniauth :facebook, 'App ID', 'App Secret'
end

ここでは、先ほど設定したFacebookアプリの「App ID」「APP Secret」の2つを付け足しましょう。

p2me_―_設定_-_開発者向けFacebook.png

5. deviseにメソッドを追加

deviseの機能を拡張させる。
app/model/user.rbのファイルの末尾に「:omniauthable」を追加。

user.rb
devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :trackable, :validatable, :omniauthable

6. Userモデルにfindメソッド実装

次に、app/model/user.rbモデルのファイルに、

user.rb
class User < ApplicationRecord
  # ...

  def self.find_for_oauth(auth)
    user = User.where(uid: auth.uid, provider: auth.provider).first

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

7. コールバック処理の実装

続いて、コールバック処理です。
app/controller以下に、usersフォルダを作成しましょう。
そのusersフォルダに、

omniauth_callbacks_controller.rb

というファイルを作ります。このファイルはこんな感じで編集します。

omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    callback_from :facebook
  end

  private
  def callback_from(provider)
    provider = provider.to_s

    # userモデルに定義したfinde_for_oauthメソッドを使用
    @user = User.find_for_oauth(request.env['omniauth.auth'])

    if @user.persisted?
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user, event: :authentication
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
end

8. ルーティング設定

コールバック用のルーティングを設定。
config/routes.rbのdevise_forの後ろに、コールバック用の記述を追記する。

routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
  # ...
end

http://localhost3000/users/sign_up
にアクセス。

cannot load such file

エラーに遭遇。。
だが、サーバーを立ち上げなおすと解決!

ログイン_-_ピトミー_ソーシャル自己分析サービス.png

よし、Sign in with Facebookでログイン。

Action_Controller__Exception_caught.png

あら、またまたエラー。

current_sign_in_at メソッドが定義されてないよ

というエラーですね。

エディタで検索をかけるとDeviseのmigrationファイルでヒットしました。

2018...._devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|

    #...
      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.string   :current_sign_in_ip
      # t.string   :last_sign_in_ip

    #...
  end
end

current_sign_in_at はTrackableに属しています。
つまり、おそらくuserモデルでTrackableを有効にしていることが原因でしょうか。
消してみましょう。

user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable
end

user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable
end

というように変更し、サイドFacebookログインを試みると ...

無事ログインできました!
万歳。

ですが、またまた問題が発生。
データベースにユーザが登録されません。

user.rb
def self.find_for_oauth(auth)
    user = User.where(uid: auth.uid, provider: auth.provider).first
    unless user
     user = User.new(
       uid: auth.uid,
       provider: auth.provider,
       email: auth.info.email,
       password: Devise.friendly_token[0, 20]
     )
    if user.save
      p "success"
    else
      p "failed"      
      p user.errors
    end
   end
   user
end

user.rbファイルを編集しました。
変更点はcreateメソッドではなくnew + saveメソッドを使うというところ。
createメソッドはnewメソッドとsaveメソッドを同時に行うメソッドであるため、これらに分解することで、エラーの発生箇所を特定することができます。

すると、エラーの際p user.errorsにより、エラー内容が出力されるので、確認すると、エラーの原因はバリデーションでした

facebookログインの時は、バリデーションを無視させなければならないようです。
(そのほかに、良い方法があれば教えていただければ嬉しいです!)

よって、次のようにsaveのオプションとして、validate: falseと追記すればデータがストアされました。

user.rb
  def self.find_for_oauth(auth)
    user = User.where(uid: auth.uid, provider: auth.provider).first

    unless user
      user = User.new(
        uid: auth.uid,
        provider: auth.provider,
        email: auth.info.email,
        password: Devise.friendly_token[0, 20],
        image: auth.info.image
      )
      user.save(:validate => false)
    end
    user
  end

エラーの原因特定のために記述したif文は消しております。

終わりに

facebook認証の実装はコーディング以外にfacebook developerへの登録も必要ですので、少し手間はかかりますが、ぜひアプリケーションには導入したい機能ですよね。

何かご質問などございましたら、コメント欄などにてお願いします。
少しでも役にたったよー。って方は是非いいねお願いします(^^)

Rails関連記事

正規表現まとめ(基礎)[Ruby編]
配列で利用できる主なメソッドをまとめてみた[Ruby編]
Mysql2::Error: Duplicate entry for key.. エラーを撃退した話(validationの設定)
Rails5でJqueryを利用しようとして少しハマった件(Uncaught ReferenceError: $ is not defined)
change_columnでの設定はrollbackできない話(This migration uses change_column, which is not automatically reversible.)[Rails: migration]
sessionに保存されたHashを別アクションで利用しようとした際にデータ型の変更によりハマった件[Rails]

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13