わたろぐ

仕事、読書、ガジェット、グルメ、写真、旅行など雑多な備忘

Ruby on Railsを触ってみる ④devise+OmniAuthでTwitter認証

今のままではユーザー関係なく、アップロードされたツイートがすべて参照できてしまう。そこでユーザ認証機能を追加して、ユーザ自身がアップロードしたツイートのみを表示できるようにしたい。

ユーザ認証はdeviseというライブラリが定番らしい。そしてTwitter認証を行うにはOmniAuthを使うのが定番らしい。早速これらを導入してみようと思う。

Gemfileを編集する。

$ git diff Gemfile
diff --git a/Gemfile b/Gemfile
index 0b509d6..587addf 100644
--- a/Gemfile
+++ b/Gemfile
@@ -48,3 +48,8 @@ end
 
 # Pagenation
 gem 'kaminari'
+
+# User Authentication
+gem 'devise'
+gem 'omniauth-twitter'
+

bundle installを行う。

$ bundle install

deviseのインストール。

$ rails g devise:install

認証用モデルUserを作成する。

$ rails g devise user

migrationファイルを編集しておく。TrackableとOmniauthableがあればいいみたいなので、不要な箇所は削除。uidにindexを張る。

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|

      ## Trackable
      t.integer  :sign_in_count, :default => 0, :null => false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip
      
      ## Omniauthable
      t.integer :uid, :limit => 8 #bigintにする
      t.string :name
      t.string :provider
      t.string :password

      t.timestamps
    end

    add_index :users, :uid,  :unique => true
  end
end

これでdb:migrateを行う。

$ rake db:migrate

予め取得していたTwitter APIのtokenをconfiginitialize/devise.rbに追記します。

  config.omniauth :twitter, '<ID>', '<SECRET>', :display => 'popup'
end

twitterからのコールバックのためのルーティングを設定する。 また、サインイン後、サインアウト後にrootに飛ばされるため、rootをひとまず設定しておく。

$ git diff config/routes.rb 
diff --git a/config/routes.rb b/config/routes.rb
index 379330e..a18f6b9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,6 +5,14 @@ Twice::Application.routes.draw do
       post 'import_csv'
     end
   end
+  
+  devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
+  root :to => 'tweets#index'
+
+  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

コールバック用のコントローラを作成する。 登録済みのユーザの場合は認証、登録済みでない場合はUsersテーブルに登録を行ってくれるはず。

/* omniauth_callbacks_controller.rb */
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def twitter
    # You need to implement the method below in your model
    @user = User.find_for_twitter_oauth(request.env["omniauth.auth"])

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

登録済みかどうかのチェックをUserモデル内に定義する。

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :trackable, :omniauthable
  
  def self.find_for_twitter_oauth(auth)
    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,
                          password:Devise.friendly_token[0,20]
                          )
    end
    user
  end
end

ログインボタンをnavigation barに追加する。

$ git diff app/views/layouts/application.html.erb 
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 67644c1..22313f4 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -52,6 +52,11 @@
               <li><%= link_to "Link1", "/path1"  %></li>
               <li><%= link_to "Link2", "/path2"  %></li>
               <li><%= link_to "Link3", "/path3"  %></li>
+              <% if user_signed_in? %>
+                <li><%= link_to "ログアウト", (destroy_user_session_path)  %></li>
+              <% else %>
+                <li><%= link_to "ログイン", (user_omniauth_authorize_path :twitter)  %></li>
+              <% end %>
             </ul>
           </div><!--/.nav-collapse -->
         </div>

実行画面はこんな感じ。ログインボタンが表示されている。

ログインボタンを押すとおなじみの認証画面。

認証を行うと、ログイン成功のメッセージが表示される。

Usersテーブルを確認すると、ちゃんと追加されていた。

devise+omniauth twata701/twice GitHub