OmniAuthを利用したソーシャルログイン機能の実装方法
【結論】
・ソーシャルログイン機能を実装するには、
コールバック機能やsessionといったrailsの幅広い
知識が無いとエラーに対応出来ない。
・コピペでは動かない
【目次】
- ソーシャルログインを実装したが・・・
- 0:deviseでアカウント機能を実装
- 1:Gemfileの導入
- 2:Userテーブルにカラムを追加
- 3:アプリIDとapp secretを取得
- 4:アプリIDとapp secretを設定
- 4:deviseのコントローラーを生成
- 5:コールバック時の処理を記述する
- 6:コールバック用コントローラーで呼び出すメソッドを定義する
- 7:SNS認証で登録する際のコントローラーの挙動を編集する
- 8:SNS認証用のリンクを貼る
- 総括
【本題】
ソーシャルログインを実装したが・・・
先日から取り組んでいたソーシャルログインが、
とりあえずローカル環境で動作出来るところまで実装できました。
但し、かなり無理矢理に実装したので、
実運用には耐えられないかもしれないです・・・
今回は、その実装例をまとめました。
基本的に、下記のサイトを参考にしています。
0:deviseでアカウント機能を実装
まず、deviseでアカウント認証機能を実装します。
1:Gemfileの導入
今回は、omniauthを利用してソーシャルログインを実装するので、
omniauthのgemを導入します。
omniauthは、メインのgemと各ソーシャルメディア毎のgemがあるので、
それぞれを導入します。今回は、FacebookとGoogleの二つにしています。
また、ソーシャルメディアのアクセスキーを環境変数で扱うのですが、
今回は、以前導入したdotenv-railsをそのまま利用します。
gem 'dotenv-rails' gem 'omniauth' gem 'omniauth-facebook' gem 'omniauth-google-oauth2'
2:Userテーブルにカラムを追加
OmniAuthによるSNS連携では、
サービス名を「provider」、各サービスのユーザーIDを「uid」
というカラム名で管理します。
なので、それらを保存する為のカラムを新たに追加する必要があります。
下記のコマンドでカラムを追加するマイグレーションファイルを生成し、
マイグレーションを実行します。
rails g migration AddOmniauthToUsers provider:string uid:string rake db:migrate
3:アプリIDとapp secretを取得
各ソーシャルメディアにアクセスするには、
開発者用のIDとシークレットキーが必要になります。
下記の開発者用ページから、取得が可能です。
取得方法は、下記のQiitaが参考になります。
4:アプリIDとapp secretを設定
deviseの設定ファイルにOmniAuth連携用の設定を記述します。
・config/initializers/devise.rb
config.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'] config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
環境変数を用いますが、dotenv-railsを導入済みなので、
ルートディレクトリに「.env」ファイルを作成し、そちらに環境変数を記述します。
・.env
FACEBOOK_KEY = '223**************************9' FACEBOOK_SECRET = 'c761***********************************bd9' GOOGLE_CLIENT_ID = '771*************************************************************com' GOOGLE_CLIENT_SECRET = 'M*********************L'|
4:deviseのコントローラーを生成
SNSへアクセスして、正常に認証された後の処理は
deviseのコントローラーが担いますが、このままだと上手く連携させられない為、
deviseのコントローラーの挙動を変える必要があります。
その為、ターミナルより、下記コマンドを実行して、deviseのコントローラーを作成します。
rails g devise:controllers users
更に生成したコントローラーを有効にする為に、
ルーティングを再定義します。
・config/routes.rb
devise_for :users, controllers: { registrations: 'users/registrations' , omniauth_callbacks: 'users/omniauth_callbacks' }
この辺りの詳しい事は、過去記事で取り上げています。
https://ryoutaku-jo.hatenablog.com/entry/2019/02/05/192803ryoutaku-jo.hatenablog.com
5:コールバック時の処理を記述する
※この辺りから我流で無理矢理動かすコードになっています。
SNS認証されたコールバック時の処理は、
先ほど生成された「omniauth_callbacks_controller.rb」で記述できます。
・app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook callback_from :facebook end def google_oauth2 callback_from :google end private def callback_from(provider) provider = provider.to_s @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[:nickname] = @user.nickname session[:email] = @user.email session[:password] = @user.password session[:provider] = @user.provider session[:uid] = @user.uid redirect_to new_user_registration_sns_path end end end
「@user = User.find_for_oauth(request.env['omniauth.auth'])」という記述で、
SNSから取得したユーザー情報を@user に渡します。
find_for_oauthメソッドは、モデルで後ほど定義します。
なお、ここでの記述内容は、
公式のwikiや、Qiitaなどで紹介されている方法とかなり異なる部分があります。
具体的に違うのは、下記2点です。
・sessionの記述
公式では、「session["devise.facebook_data"]」としていましたが、
これだと、ニックネームなど個々の値を取得するのが面倒だったので、
sessionに格納する時点で、キー毎に分けています。
・リダイレクト先のパス
公式は「new_user_registration_path」ですが、
「new_user_registration_sns_path」にしています。
本家メルカリだと、SNSで登録する場合、
メールアドレスとパスワードの入力が省略されますが、
通常の登録フォームと兼用させるのが難しかったので、
別にビューを用意して、そちらに飛ぶようにしています。
6:コールバック用コントローラーで呼び出すメソッドを定義する
まずUserモデルにOmniAuthを有効化する記述を以下の通り追加します。
・app/models/user.rb
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: %i[facebook google_oauth2]
次にomniauth_callbacks_controllerで利用する
SNS認証で得られたユーザー情報を取得するメソッドを定義します。
・app/models/user.rb
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, nickname: auth.info.name, email: User.dummy_email(auth), password: Devise.friendly_token[0, 20] ) end user end private def self.dummy_email(auth) "#{auth.uid}-#{auth.provider}@example.com" end
ここも公式と大きく違う点があります。
それは、メールアドレスの記述です。
公式通り「auth.info.email」で取得を試みたのですが、
一向に取得できず、scopeや自身のFacebookアカウントなど、
色々調査したのですが、八方塞がりでした・・・
そこで、メールアドレスの取得は諦めて、ダミーのメールアドレスを生成する事にしました。
uidはprovider毎に一意の値なので、それを組み合わせたメールアドレス("#{auth.uid}-#{auth.provider}@example.com")を
userのメールアドレスとする記述になっています。
7:SNS認証で登録する際のコントローラーの挙動を編集する
sessionに保存した値を、フォームに出力して、
フォームをsubmitした際に、sns認証用の情報もDBに保存される様に記述します。
なお、こちらの記述は公式にはありません。
class Users::RegistrationsController < Devise::RegistrationsController def sns @user = User.new( nickname: session[:nickname], email: session[:email], password: session[:password], password_confirmation: session[:password], ) end def create super @user.uid = session[:uid] @user.provider = session[:provider] @user.save end end
8:SNS認証用のリンクを貼る
最後に、SNS認証用のリンクを貼ります。
= link_to "Facebookでログイン", user_facebook_omniauth_authorize_path = link_to "Googleでログイン", user_google_oauth2_omniauth_authorize_path
以上で、作業完了です。
総括
初めはコピペで簡単に出来ると高をくくっていましたが、
コールバック機能やsessionなど、railsの幅広い知識が無いと
エラーに対処できないので、かなり苦戦しました。
正直、これで良いのか分かりませんが、
とりあえず登録・ログインは出来たので、一旦は良しとしたいと思います。