Devise + OmniAuthで複数サービスからログインするメモ

いつも同じことするたびに手間がかかるのでいい加減にメモっておく「Devise + OmniAuth」編。

DeviseとOmniAuthで複数のソーシャルサービス経由でログインできるようにする。

環境

Rails 4.2.7
ruby 2.3.3

Gemfile

gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-twitter'

$ bundle install

Authentication モデルを作成

$ bundle exec rails g model Authentication user_id:integer provider:string uid:string

migrationファイル

class CreateAuthentications < ActiveRecord::Migration
  def change
    create_table :authentications do |t|
      t.references :user, index: true, foreign_key: true, null: false
      t.string :provider, null: false
      t.string :uid, null: false

      t.timestamps null: false
    end
  add_index :authentications, [:provider, :uid], unique: true
  end
end

$ bundle exec rake db:migrate

app/models/authentication.rb

class Authentication < ActiveRecord::Base
  belongs_to :user

  validates :user_id, :uid, :provider, presence: true
  validates :uid, uniqueness: { scope: :provider }
  validates :provider, inclusion: { in: ["twitter", "facebook"] }

end

app/models/users.rb

class User < ActiveRecord::Base
 devise :omniauthable, :omniauth_providers => [:facebook, :twitter]
 validates :name, presence: true, length: { maximum: 24 }, uniqueness: true, case_sensitive: false, format: { with: /\A[a-zA-Z0-9_]+\Z/ }

 has_many :authentications, :dependent => :destroy
:

app/controller/application_controller.rb

  protected
    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :email, :password, :password_confirmation])
      devise_parameter_sanitizer.permit(:sign_in, keys: [:name, :password])
      devise_parameter_sanitizer.permit(:account_update, keys: [:name])
    end

app/controller/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController

  def build_resource(hash=nil)
    if hash.empty?
      if session[:omniauth].try(:[], 'provider') == 'facebook'
        hash[:email] = session[:omniauth]['info']['email']
        hash[:name] = session[:omniauth]['info']['name']
      elsif session[:omniauth].try(:[], 'provider') == 'twitter'
        hash[:name] = session[:omniauth]['info']['name']
      end
    end
    super
  end

  def create
    super
    if session[:omniauth].try(:[], 'provider') && @user.persisted?
      @user.authentications.create(provider: session[:omniauth]['provider'], uid: session[:omniauth]['uid'])
      session.delete(:omniauth)
    end
  end

app/controller/authentications_controller.rb

class AuthenticationsController < ApplicationController
 skip_before_action :require_login, only: :create

 def all
 omniauth = request.env["omniauth.auth"]
 authentication = Authentication.find_by_provider_and_uid(omniauth.provider, omniauth.uid)
 if authentication && current_user.nil?
 flash[:notice] = "ログインしました。"
 sign_in_and_redirect(authentication.user)
 elsif authentication && authentication.try(:user_id) != current_user.id
 flash[:notice] = "この#{omniauth.provider}アカウントは、別のアカウントと既に連携されています。"
 redirect_to edit_user_registration_url
 elsif current_user
 current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
 flash[:notice] = "アカウントの #{omniauth.provider} 連携を設定しました。"
 redirect_to root_url()
 else
 session[:omniauth] = omniauth.except('extra')
 redirect_to new_user_registration_url
 end
 end

 # DELETE /authentications/:id
 def destroy
 provider = params[:provider]
 authorization = current_user.authentications.find_by_provider_and_user_id(provider, current_user.id)
 authorization.destroy
 redirect_to root_url, notice: "アカウントの #{provider} 連携を解除しました。"
 end

 alias_method :facebook, :all
 alias_method :twitter, :all
end

routes.rb

  devise_for :users, controllers: {
    registrations: 'users/registrations',
    sessions: 'users/sessions',
    omniauth_callbacks: 'authentications'
  }

  get '/auth/:provider/callback' => 'authentications#facebook'
  delete "/auth/destroy/:provider" => 'authentications#destroy', as: :destroy_connection

  devise_scope :user do
    get "sign_in", :to => "users/sessions#new"
    get "sign_out", :to => "users/sessions#destroy"
  end

アプリ登録

facebook https://developers.facebook.com/

APP_ID: ******
APP_SECRET: ******
Facebookログイン > クライアントOAuth設定> 有効なOAuthリダイレクトURI: http://hogehoge.dev http://hogehoge.com

Twitter https://apps.twitter.com/

Consumer Key (API Key) ******
Consumer Secret (API Secret) ******
Callback URL: http://hogehoge.com/auth/twitter/callback

config/initializers/devise.rb

  config.omniauth :twitter,  ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
  config.omniauth :facebook, ENV['FACEBOOK_ID'], ENV['FACEBOOK_SECRET']

edit.html.haml

%h3= "SNS Account"
- if current_user.authentications.find_by_provider(:facebook)
  %p= link_to "unconnect facebook", destroy_connection_path(:facebook), method: :delete, data:{confirm:"Sure?"}
- else
  %p= link_to "connect facebook", user_facebook_omniauth_authorize_path

- if current_user.authentications.find_by_provider(:twitter)
  %p= link_to "unconnect twitter", destroy_connection_path(:twitter), method: :delete, data:{confirm:"Sure?"}
- else
  %p= link_to "connect twitter", user_twitter_omniauth_authorize_path

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です