読者です 読者をやめる 読者になる 読者になる

OmniAuth + DeviceでTwitter, Facebook連携

0. やりたいこと

Railsアプリでコンテンツ登録時に
Twitterのタイムラインに拡散する
FacebookFacebookページに拡散する

1. 前提

・deviceでユーザー登録機能が実装されている
 Rails 4.0.0.beta1でdevise - t-taira blog
Twitter Developersに登録している
 Twitter Developers
Facebook Developersに登録している
 Home - Facebook Developers

2. 設定関連

・GemFile

# twitter
gem 'twitter'
gem 'omniauth-twitter'

# facebook
gem 'fb_graph'
gem 'omniauth-facebook'

gem 'activerecord-session_store'

・config/routes

  devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } do
    delete '/users/disconnect/:provider',
      :to => 'users#disconnect_omniauth_provider',
      :as => 'disconnect_omniauth_provider'
  end
  resources :users

・config/initializers/devise.rb

# ココ追加
config.omniauth :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET', :display => 'popup'
config.omniauth :facebook, 'APP_ID', 'APP_SECRET', :scope => 'manage_pages', :display => 'popup'
  
DeviseController.class_eval do
  before_action :resource_params #, only: [:show, :edit, :update, :destroy]

  def resource_params
    unless params[resource_name].blank?
      params.require(resource_name).permit(
	    # ココ追加
        :twitter_id, :twitter_token, :twitter_secret,
        :facebook_id, :facebook_token, :facebook_secret,

    ・・・
      )
    end
  end
end

3. Model

・app/models/user

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
    :recoverable, :rememberable, :trackable, :validatable, :confirmable,
    :omniauthable, :omniauth_providers => [:facebook, :twitter] # ココ追加

  # 連携済みかどうか
  def connected?(provider)
    return eval("self.#{provider}_id.nil?")
  end

  # 以下、コールバックで呼ばれるメソッド追加
  def self.find_for_facebook_oauth(access_token, signed_in_resource = nil)
    data = access_token.extra.raw_info
    User.where(:facebook_id => data.id).first
  end

  def self.find_for_twitter_oauth(access_token, signed_in_resource = nil)
    data = access_token
    User.where(:twitter_id => data.uid).first
  end

  def connect_with provider, authdata
    logger.info("AUTH: #{authdata.inspect}")

    case provider
    when :twitter
      self.twitter_id = authdata['uid']
      self.twitter_token = authdata['credentials']['token']
      self.twitter_secret = authdata['credentials']['secret']
    when :facebook
      self.facebook_id = authdata['uid']
      self.facebook_token = authdata['credentials']['token']
    end
    save
  end
end	
4. Controller

・app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    authorize :facebook do
      session["devise.facebook_data"] = request.env["omniauth.auth"]
    end
  end

  def twitter
    authorize :twitter do
      # http://stackoverflow.com/questions/7117200/devise-for-twitter-cookie-overflow-error
      session["devise.twitter_data"] = request.env["omniauth.auth"].except('extra')
    end
  end

  private
  def authorize provider
    @user = User.send "find_for_#{provider}_oauth", request.env["omniauth.auth"], current_user

    provider_name = provider.to_s.titleize

    # if current_user exists:
    # if @user is nil, connecting the account
    # if @user is not nil and not equal to current_user, show an error
    # else, see below:
    if current_user
      if @user.nil?
        current_user.connect_with provider, request.env['omniauth.auth']

        flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => provider_name
      elsif @user != current_user
        # TODO: I18n
        flash[:error] = "That #{provider_name} account is already linked to a different user."
      else
        flash[:notice] = "Your account is already linked with the #{provider_name} account."
      end
      redirect_to edit_user_registration_path
    else
      if @user
        flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => provider.to_s.titleize
        sign_in_and_redirect @user, :event => :authentication
      else
        yield if block_given?
        redirect_to new_user_registration_url
      end
    end
  end

end

・app/controllers/users_controller.rb

class UsersController < ApplicationController

 # 連携を解除するメソッド
 def disconnect_omniauth_provider
    provider = params[:provider]
    if Devise.omniauth_providers.include? provider.to_sym
      current_user.update_attributes(
        {
          "#{provider}_id" =>  nil,
          "#{provider}_token" =>  nil,
          "#{provider}_secret" =>  nil,          
        }
      )
      current_user.save

      flash[:success] = "Your account has been disconnected from #{provider.titleize}."
    end
    redirect_to edit_user_registration_path
  end
  
  ・・・
end  
5. View

・app/views/layouts/application.html.erb

<div class="well sidebar-nav">
  <p class="nav-header">SNS連携</p>

  <ul class="nav nav-tabs nav-stacked">
    <% Devise.omniauth_providers.each do |provider| %>

      <% if current_user.connected?(provider) %>
        <li>
          <%= link_to image_tag("authbuttons/#{provider.to_s}_32.png") + " 認証する",
            user_omniauth_authorize_path(provider) %>
        </li>
      <% else %>
        <li>
          <%= link_to image_tag("authbuttons/#{provider.to_s}_32.png") + "  解除する",
            disconnect_omniauth_provider_path(provider), :method => :delete %>
        </li>
      <% end %>
    <% end %>
  </ul>
</div>
6. 連携

Twitter

Twitter.configure do |config|
  config.consumer_key       = CONSUMER_KEY
  config.consumer_secret    = CONSUMER_SECRET
  # oauth_token, oauth_token_secretは連携時に取得した値を利用
  config.oauth_token        = current_user.twitter_token 
  config.oauth_token_secret = current_user.twitter_secret
end

twitter_client = Twitter::Client.new
# ココはRailsアプリで登録したコンテンツにする
twitter_client.update("hogehoge")

Facebook

# facebook_tokenは連携時に取得した値を利用
me = FbGraph::User.me(current_user.facebook_token)
# me.accounts[0]で最新のファンページを取得しているが...
# 本当はファンページが複数ある場合は、指定したい
page_access_token = me.accounts[0].access_token

me = FbGraph::User.me(page_access_token)
me.feed!(
 # # ココはRailsアプリで登録したコンテンツにする
  :message => 'hellow world!',
  :link => 'https://www.google.co.jp/')

Source: OmniAuth: Overview · plataformatec/devise Wiki · GitHub