最初に
webサービスを開発するにあたってユーザー認証機能はマストですよね。
いかにUXを良くしユーザに使ってもらえるサービスにするかは全ての開発者の望むところだと思います。
sns認証サービスに対応しておくとユーザの登録の手間が省け、ユーザ数の増加に繋がるはずです。
そこで、今回はfacebook認証を実装し、「サクッとログイン」を叶えていきましょう。
devise認証機能を実装済みという前提でお話していきますので、まだの方は下記記事を参考にdeviseによるログイン機能を実装しておきましょう。
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にアクセスして、「新しいアプリを追加」を選択。
続いて、表示されたモーダルウィンドウにメールアドレスとアプリ名(適当で良い)を記入。
左メニューの「設定」→「ベーシック」を選択。
遷移先ページの一番下の「+プラットホームを追加」を選択。
そこから、「Website」を選び、url記入欄「サイトURL」に下記のように記述する。
注:production環境の場合は適当なURLに変更する。
これでFacebook側の設定は終了です。
3. Gemを追加
gem 'omniauth'
gem 'omniauth-facebook'
の2行をGemfileに追記する。
そして、
$ bundle install
4. AppIDとSecretIDを追加
config/initializers/devise.rbに下記のコードを追記する。。
Devise.setup do |config|
config.omniauth :facebook, 'App ID', 'App Secret'
end
ここでは、先ほど設定したFacebookアプリの「App ID」「APP Secret」の2つを付け足しましょう。
5. deviseにメソッドを追加
deviseの機能を拡張させる。
app/model/user.rbのファイルの末尾に「:omniauthable」を追加。
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
6. Userモデルにfindメソッド実装
次に、app/model/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
というファイルを作ります。このファイルはこんな感じで編集します。
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の後ろに、コールバック用の記述を追記する。
Rails.application.routes.draw do
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
# ...
end
http://localhost3000/users/sign_up
にアクセス。
cannot load such file
エラーに遭遇。。
だが、サーバーを立ち上げなおすと解決!
よし、Sign in with Facebookでログイン。
あら、またまたエラー。
current_sign_in_at メソッドが定義されてないよ
というエラーですね。
エディタで検索をかけるとDeviseのmigrationファイルでヒットしました。
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を有効にしていることが原因でしょうか。
消してみましょう。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
↓
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable
end
というように変更し、サイドFacebookログインを試みると ...
無事ログインできました!
万歳。
ですが、またまた問題が発生。
データベースにユーザが登録されません。
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
と追記すればデータがストアされました。
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]