LoginSignup
25
27

More than 5 years have passed since last update.

Devise gemを使ってfacebook認証を実装する

Last updated at Posted at 2014-01-12

はじめに

会員登録画面を簡単に作ることの出来るRailsのdevise gemというものがあります。
このdevise gemを使ってfacebook認証を可能にするomniauthという機能があるらしいのでそれについて調べてみました。
以下はdeviseのwiki, facebook exampleの和訳です

devise gem についてはこちら

使い方

まずはomniauth gem を追加してください。Gemfileに以下を記述

gem 'omniauth-facebook'

ここではfacebookの例を示しますが、他のomniauth gemでも同じように使うことが出来ます。一般的にgemの名前は "omniauth-provider"で、porviderのところにfacebook とか twitterとかに置き換えます。
omniauth List

Userモデルにproviderとuidというカラムを追加します

rails g migration AddColumnsToUsers provider uid
rake db:migrate

次にconfig/initializers/devise.rbにproviderの宣言とrequireを書く必要があります

require "omniauth-facebook"
config.omniauth :facebook, "APP_ID", "APP_SECRET"

requestのpermissionを変えるのなら"omniauth-* "のgemのread meを読んでください。

何らかの理由によりDeviseが starategy classを読み込まない場合ははっきりと:strategy_classを明記することが出来ます。

config.omniauth :facebook, "APP_ID", "APP_SECRET", :strategy_class => OmniAuth::Strategies::Facebook

 
 
もしこのようなエラーが出たら

OpenSSL::SSL::SSLError (SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed):

必要なomniauthの証明キーがどこにあるのか明記する必要があります。certified gemかメソッドを使ってください(これはあなたの使っているOSに依存します)

config.omniauth :facebook, "APP_ID", "APP_SECRET",
      :client_options => {:ssl => {:ca_path => '/etc/ssl/certs'}}

もしこのappをherokuでデプロイしたいのならこのような設定sectionを各必要があります

config.omniauth :facebook, "APP_ID", "APP_SECRET",
      {:scope => 'email, offline_access', :client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}} 

Engin Yard Cloud serverは/etc/ssl/certs/ca-certificates.crtににあります.
OSXの開発においては証明書がfileではなくkeychainに置いてあるので簡単に証明書を無効にすることが出来ます。

require "omniauth-facebook"
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if Rails.env.development? 
config.omniauth :facebook, "APP_ID", "APP_SECRET"

errorについての深い議論は: https://github.com/intridea/omniauth/issues/260 でされています

strategyの設定をした後、model (e.g. app/models/user.r)をomniauthをつけることが必要です。

devise :omniauthable, :omniauth_providers => [:facebook]

(注意)既にrailas serverを起動していたのなら再起動してomniauthの設定を反映させることが必要です。

現在はDeviseでomniauth model を作ることを許可しただけです。Usertという名前のomniauthのでいるmodelが作られて、config/routes.rb,にdevise_for :usersが既に追加されているならDeviseは以下の様なurlメソッドを作ります。

  • user_omniauth_authorize_path(provider)
  • user_omniauth_callback_path(provider)

(注意)Deviseは *_url methodsを作る訳ではありません。決してディレクトリの上でcallback helperを使わないでください。facebook認証のためのlayoutの最初の一行に以下を記述するだけで大丈夫です

<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>

useromniauth_authorize_pathメソッドに変化する記号はDeviseの構成ブロックにmatchします。passをどうするか確認するためにも"omniauth-* " gem のREADMEをしっかり読んでください。

上のリンクをクリックするとuserはfacebookにredirecteされます。(もしこのリンクが存在しなければサーバーを再起動させようとします)証明書を得た後、あなたのapplicationのcallbackメソッドにridirectされます。callbackを実行するために一番最初にすることはconfig/routes.rb file に戻ってどのcontrollerでcallbackをじっこうするか決まることです。

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

私たちは "app/controllers/users/omniauth_callbacks_controller.rb":を新しく作ったとします。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
end

このcallbackはproviderの名前と同じactionとして実行されます。ここではfacebook providerのためにcontrollerに追記します

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end

このactionはいくつかの特徴的な側面があります

1,omniauthによってfacebookから取得する情報すべてがrequest.env["omniauth.auth"]のようなhashで手に入る。どの情報が帰ってくるのか確認のためにOmniauthとomniauth-* gemを確認してください。

2.有効なuserがmodelから送られた場合sign_inするべきです。flash messageにはdefaultのメッセージがsetされていることを覚えておいてください。次に、userがsign inしてredirectします。 :event => :authentication to the sign_in_and_redirect methodを使ったら強制的にすべてのauthenticationのcallbackメソッドがよばれます。

3.userが存在しない場合、Omniauth dataをsessionに保存します。私たちは"deviseというキー名でそれを使うことが出来ます。userがsign inしたときいつでも"devise"から始まるデータをすべて削除することが効果的です。ですから私たちは自動的にsessionをclean upします。最終的に登録フォームにredirectされます。

 
 
controllerが定義された後、find_for_facebook_oauth メソッドをmodel(app/models/user.rb)のなかで実行する必要があります

def self.find_for_facebook_oauth(auth)
  where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
    user.provider = auth.provider
    user.uid = auth.uid
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.name = auth.info.name   # assuming the user model has a name
    user.image = auth.info.image # assuming the user model has an image
    user.save!
  end
end

(注意) "User.new_with_session" によってDeviseの登録controllerが呼ばれる前にresourceが作られています。セッションからcopyする必要があるならsign_upの前にmodelのなかでnew_with_session を実行してuserがいつでも初期化されているということを意味します。これはfacebook emailをcopyする例です。

class User < ActiveRecord::Base
  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

最終的にFacebookでsign upすることをcancelすることを許可したいのなら "cancel_user_registration_path"にredirectさせることも出来ます。これはdeviseで始まるすべてのsessionデータを削除し、new_with_sessionは呼ばれない。

logout link

config/routes.rb
devise_scope :user do
   get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
end

これで必要なことはすべて終わりました。時間があればtestなんかも書いてみてください。

omniauthを他のauthoticationなしで使う

もしあなたがomniauthだけをやりたいのならrouteに new_user_session (if not defined, root will be used)を定義する必要があります。下のものはその例です。(omniauth で他のauthoticationやdbを使うならこれは必要ありません)
```
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }

devise_scope :user do
get 'sign_in', :to => 'devise/sessions#new', :as => :new_user_session
get 'sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
end
```
上の例ではsession controllerでは特別に何かをする必要はありません。

25
27
0

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
25
27