Ruby: Rails アプリに Devise と Omniauth で Facebook/Twitter アカウントでもいけるログイン機能を実装

Devise ログイン機能を実装できる gem。
Omniauth Auth 周りをなんとかしてくれる gem。omniauth-facebook/omniauth-twitter とかを追加する。
foursquare、github、google、hatena、instagram、mixi、nikeplus、paypal、steam、wunderlist、vimeo、wordpress、…
いろんな Auth プラグインが rubygem には公開されているっぽい。

 

Gemfile に Devise と Omniauth を追加

gem 'devise'
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
$ bundle install

Devise で設定ファイルの作成(config/initializers/devise.rb)

$ rails g devise:install

認証用モデル(User)作成

$ rails g devise user
$ rake db:migrate
$ vi config/initializers/devise.rb
config.secret_key = '指定されたシークレットキー'

omni 用の uid:integer を作成

$ rails g migration AddOmniToUser uid:integer provider name
$ vi db/migrate/*_add_omni_to_user.rb
, :limit => 8
$ rake db:migrate

http://localhost:3000/users/sign_in とかでサインイン画面が開くようになる

$ rails g devise:views users
$ rails g controller users/sessions
$ rails g controller users/passwords
$ rails g controller users/registrations
$ rails g controller users/omniauth_callbacks

各 controller を編集

app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
    def cancel
        super
    end

    def create
        super
    end

    def new
        super
    end

    def edit
        super
    end

    def update
        super
    end

    def destroy
        super
    end

    def build_resource(hash=nil)
        hash[:uid] = User.create_unique_string
        super
    end
end

app/controllers/users/omniauth_callbacks_controller.rb

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"], current_user)

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

    def twitter
        # You need to implement the method below in your model
        @user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)

        if @user.persisted?
            set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
            sign_in_and_redirect @user, :event => :authentication
        else
            session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
            redirect_to new_user_registration_url
        end
    end
end

modelを編集

app/models/user.rb

class User < ActiveRecord::Base
    # Include default devise modules. Others available are:
    # :confirmable, :lockable, :timeoutable and :omniauthable
    devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook, :twitter]

    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,
                               password: Devise.friendly_token[0,20]
                              )
        end
        user
    end

    def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
        user = User.where(:provider => auth.provider, :uid => auth.uid).first
        unless user
            user = User.create(name:     auth.info.nickname,
                               provider: auth.provider,
                               uid:      auth.uid,
                               email:    User.create_unique_email, #適当
                               password: Devise.friendly_token[0,20]
                              )
        end
        user
    end

    def self.create_unique_string
        SecureRandom.uuid
    end

    def self.create_unique_email
        User.create_unique_string + "@example.com"
    end
end

initializer を編集

  # API key
  if Rails.env.production?
    config.omniauth :facebook,  "App ID", "App Secret"
    config.omniauth :twitter,   "Consumer key", "Consumer secret"
  else
    config.omniauth :facebook,  "App ID", "App Secret"
    config.omniauth :twitter,   "Consumer key", "Consumer secret"
  end

config/routes.rb を編集

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

app/views/layouts/application.html.erb の body に以下を追加

<p class="notice"><%= notice %></p>                                                                                                                                                      
<p class="alert"><%= alert %></p>

<p class="">
<% if user_signed_in? %>
    Logged in as <strong><%= current_user.email %></strong>.
    <%= link_to 'Edit profile', edit_user_registration_path, :class => 'navbar-link' %> |
    <%= link_to "Logout", destroy_user_session_path, method: :delete, :class => 'navbar-link'  %>
<% else %>
    <%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link'  %> |
    <%= link_to "Login", new_user_session_path, :class => 'navbar-link'  %>
<% end %>

app/views/users/shared/_links.html.erb を編集

     <%= link_to "Sign in with #{provider.to_s.titleize}", user_omniauth_authorize_path(resource_name, provider) %><br />                                                                                    

     <%= link_to "Sign in with #{provider.to_s.titleize}", user_omniauth_authorize_path(provider) %><br />                                                                                    

Facebook の設定

Devise.setup do |config|
  # 最後に追加
  if Rails.env.production?                                                                                                                                                                   
    config.omniauth :facebook,  "App ID", "App Secret"
    config.omniauth :twitter,   "APIキー", "APIシークレット"
  else
    config.omniauth :facebook,  "App ID", "App Secret"
    config.omniauth :twitter,   "APIキー", "APIシークレット"
  end
end

Facebook アプリをこちらで作成
ここで status を ON にすると公開される

https://developers.facebook.com/apps/870158639700961/review-status/

Twitter の設定

localhost は URL と認識されないので Callback URL の部分は http://127.0.0.1/callback とかしておく