Devise + OmniAuth + OmniAuth Facebookで Facebook 認証

はじめに

Devise を使って実装した認証機能を、Facebook の OAuth を使ったものに変更したい。そこで OmniAuth と OminiAuth Facebook を導入して、認証機能を修正してみた。

OmniAuth の Wiki 通りにやれば簡単にいくと思っていたら、ちょっと躓いたので、手順をメモしておく。

ちなみに、参考にした Wiki はこちら。

Facebook API キーとシークレットキーを取得

Facebook Developers にログインして、アプリを登録する。

アプリを登録したら、アプリ一覧で API キーとシークレットキーを確認できる。

登録する際、「Facebookでログインするウェブサイト」にひとまず次の URL を設定しておく。

http://localhost:3000

これを設定していなかったら、開発環境でデバッグしたとき、Facebook でログインしようとしてリダイレクトエラーになってしまった。

アプリ公開時は、正式なアプリの URL に変更しておく。

OmniAuth と OmniAuth Facebook をインストール

Gemfile に

gem "omniauth"
gem "omniauth-facebook"

を追加して

bundle

を実行。

users テーブルに Facebook 用の列を追加

Devise で認証機能を実装したとき、既に users テーブルを作成していたので、Facebook で認証するのに必要な列を追加するマイグレーションファイルを作成する。

bundle exec rails g migration AddFacebookFieldsToUsers name:string uid:integer provider:string access_token:string

マイグレーション実行。

bundle exec rake db:migrate

User クラスを修正。

Facebook 認証後、データベースにユーザー情報が登録されていなければ登録する。Graph API を使いたいので、アクセストークンも保存しておく。

class User < ActiveRecord::Base
  # omniauthable を追加し、使わないものは削除。
  devise :database_authenticatable, :trackable, :omniauthable

  # name, provider, uid を追加
  attr_accessible :email, :password, :name, :provider, :uid, :access_token

  def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth.provider, :uid => auth.uid).first
    unless user
      user = User.create({
        :name => auth.extra.raw_info.name,
        :provider => auth.provider,
        :uid => auth.uid,
        :email => auth.info.email,
        :access_token => auth.credentials.token,
        :password => Devise.friendly_token[0, 20],
      })
    end
    user
  end

  def self.new_with_session(params, session)
    super.tap do |user|
      if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
        user.email = data["email"] if user.email.blank?
      end
    end
  end
end

Devise の設定

config/initialzers/devise.rb を編集する。

Devise.setup do |config|
  # ...

  config.omniauth :facebook, "APPKEY", "APPSECRET"

  # ...
end

APPKEY と APPSECRET は、Facebook Developers でアプリを登録したとき発行されたものに変更すること。

コントローラを作成

app/controllers/users/omniauth_callbacks_controller.rb を作成。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
      sign_in_and_redirect @user, :event => :authentication
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end

ルーティングの設定

config/routes.rb の devise_for を使っている箇所を修正。

 devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }

最後にビューを編集

app/views/layouts/application.html.erb にサインイン&サインアウトのリンクを追加。

<ul class="nav pull-right">
  <% if user_signed_in? %>
    <li><%= link_to "Sign out", destroy_user_session_path, :method => :delete %></li>
  <% else %>
    <li><%= link_to "Sign in", user_omniauth_authorize_path(:facebook)  %></li>
  <% end %>
</ul>

ブラウザで確認できたら OK

サーバーを起動。

bundle exec rails s

ブラウザで localhost:3000 にアクセスし、Sign in をクリック。すると Facebook の認証ページに移動するので、ログインしてアプリを許可し、アプリにリダイレクトされたら成功。