LoginSignup
1
2

More than 3 years have passed since last update.

【Rails6】Devise+SNS認証で登録&ログイン(複数連携可)

Posted at

はじめに

複数のSNSと連携可能な認証をつくります。
Deviseは実装済みとして進めます。
各APIキーの取得等にも触れませんので適宜お調べ下さい。
今回はfacebookとtwitterで実装しますが他のSNSでも基本的には同じだと思います。

環境

環境
Windows10
ruby 2.6.6
Rails 6.0.3.1

下準備

gem追加
Gemfile
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-twitter'

保存後bundle installして下さい。

credentials.ymlにAPIキーを記述

Windowsの方は前回の記事を参考にしてみてください。

credentials.yml

facebook:
  api_key: pk_test_~
  secret_key: sk_test_~
twitter:
  api_key: pk_test_~
  secret_key: sk_test_~
Deviseの設定

config/initializer内。

devise.rb
#以下を追加
config.omniauth :facebook, Rails.application.credentials.facebook[:api_key], Rails.application.credentials.facebook[:secret_key], scope: 'email', info_fields: 'email,name'
config.omniauth :twitter, Rails.application.credentials.twitter[:api_key], Rails.application.credentials.twitter[:secret_key], scope: 'email', callback_url: 'https://localhost:3000/users/auth/twitter/callback'

twitterの方はコールバックURLを明示する必要があるみたいです。

Socialモデルを作成

今回はDeviseでUserモデルを作成済みとします。
Userモデルへカラムを追加してもいいですが、今回はUserモデルに紐づくSocialモデルを新たに作成します。
(UserモデルとSocialモデルは一対多の関係となります。)

console
rails g model social

Socialモデルにはuser_id、provider("facebook"や"twitter"などが入る)、uid(各SNSアカウントと紐づけるためのidが入る)を持たせます。

2020~create_socials.rb
class CreateSocials < ActiveRecord::Migration[6.0]
  def change
    create_table :socials do |t|
      t.references :user, null: false, foreign_key: true
      t.string :provider, null: false
      t.string :uid, null: false
      t.timestamps
    end
  end
end

保存したらrails db:migrateする。

関連付け
user.rb
has_many :socials, dependent: :destroy#追加
devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, 
          :omniauthable#追加
social.rb
belongs_to :user
validates :uid, uniqueness: {scope: :provider}#同一provider内で多重登録できないようにする

同じSNSアカウントが複数のユーザーに紐付かないようにします。
「{scope: :provider}」は多分なくても平気だと思いますが念のため。

実装

コールバック処理をつくる
routes.rb
devise_for :users, controllers: {
  sessions: 'users/sessions',
  password: 'users/password',
  registrations: 'users/registrations',
  omniauth_callbacks: 'users/omniauth_callbacks'#追加
  }

以下app/controllers/users/omniauth_callbacks_controller.rb内。
(rails routes等で確認し、この中のメソッドを各リダイレクト先に設定して下さい。)

omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

 def facebook
    if request.env['omniauth.auth'].info.email.blank?#Facebookでメール使用を許可しているか
      redirect_to '/users/auth/facebook?auth_type=rerequest&scope=email'
    end
    callback_from :facebook
  end

  def twitter
    callback_from :twitter
  end


  private

  def callback_from(provider)
    provider = provider.to_s

    if user_signed_in?
      @user = current_user
      User.attach_social(request.env['omniauth.auth'], @user.id)#後でattach_social作る。SNSからの情報とログイン中のUserのidを渡す。
    else
      @user = User.find_omniauth(request.env['omniauth.auth'])#後でfind_omniauth作る。SNSからの情報を渡す。
    end

    if @user.persisted?#登録済みor登録できたら
      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

リダイレクト元のSNSの種類を「callback_from」へ渡して切り分けます。
「callback_from」内では「SNS認証での新規ユーザー登録またはSNS認証済みユーザーのログイン」か「ログイン中ユーザーのSNS連携」かを切り分けました。
「登録後」「ログイン後」「連携後」の処理は今回は分けずに、全てユーザーページにリダイレクトさせています。

次にUser.rb内に各処理を書きます。

user.rb
 def self.find_omniauth(auth)#SNS認証での新規登録またはsnsログイン
    social = Social.where(uid: auth.uid, provider: auth.provider).first
    unless social.blank?#sns認証済み(ログイン)
      user = User.find(social.user.id)
    else#sns認証での新規登録
      temp_pass = Devise.friendly_token[0,20]#今回は取り敢えずランダムなパスワードを作ります
      user = User.create!(
        username: auth.info.name,
        email: auth.info.email,
        password: temp_pass,
        password_confirmation: temp_pass,
      )
      social = Social.create!(
        user_id: user.id,
        provider: auth.provider,
        uid: auth.uid,
      )
    end

    return user
 end

 def self.attach_social(auth, user_id)#sns連携追加時
    social = Social.create!(
      user_id: user_id,
      provider: auth.provider,
      uid: auth.uid,
    )
  end

「SNS認証での新規ユーザー登録またはSNS認証済みユーザーのログイン」時はさらに「新規登録」か「ログイン」かを切り分けます。
「新規登録」時はUserとSocialを両方作成し(同時にUserとSocialの紐づけもする)、Userを返します。
「ログイン」時はSNSから送られてきた情報(auth.uidとauth.provider)からSocialを探し、紐づくUserを返します。
「ログイン中ユーザーのSNS連携」時はSocialのみ作成します。ログイン中のUserと紐づけるため、user_idを受け取ってます。

一応これで処理は終わりです。あとは「user_facebook_omniauth_authorize_path」などでリンクを作成すれば、Userの状況に応じて新規登録やログイン、連携を行ってくれます。

おしまい

実際には連携したSNSへのリンクを作ったり、SNSでの新規登録時のパスワード再設定の仕組み、Deviseのメール認証、連携の解除ボタンなどもあったほうが良いかと思いますが、ひとまず最小構成で作ってみました。
Rails勉強中なのでスマートでない所が多々あるかと思います。もしおかしなことをしてたら教えて頂けると嬉しいです!

1
2
2

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
1
2