Deviseとomniauthを使ってTwitterのOAuth認証

TwitterOAuth認証を使うようなアプリってド定番なのに、時間が空くと、ワリと忘れちゃって時間をロスするので手順化しておこうと思う。
なお、usersとauthenticationsにユーザ情報を分けてるのは、Facebook認証とか追加するとき用、usersがauthenticationsが1対nになる。

[環境]
Mac OSX 10.6.8
ruby 1.8.7
rails 3.0.6

[参考]
http://blog.twiwt.org/e/14b25f
http://taksatou.blogspot.com/2011/03/twitterfacebookrails.html
http://taksatou.blogspot.com/2011/03/twitterfacebookoauthrailsomniauth.html



・Gemfileに追加

gem 'devise'
gem 'omniauth', '~> 0.2.6'


・bundleする

$ bundle install


・config/omniauth.rbをつくる

Rails.application.config.middleware.use OmniAuth::Builder do
  if Rails.env == 'development'
    provider :twitter, 'TOKEN', 'SECRET'
  else
    provider :twitter, 'TOKEN', 'SECRET'
  end
end


・Deviseのセットアップ

$ rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml

$ rails g devise user
      invoke  active_record
      create    db/migrate/20110816040643_devise_create_users.rb
      insert    app/models/user.rb
       route  devise_for :users

$ rails g model Authentication


★model/user.rbを編集(色々コメントアウト)

require 'digest/sha1'

class User < ActiveRecord::Base
  has_many :authentications

  devise :trackable, :omniauthable

end

★model/authentication.rbに追記

class Authentication < ActiveRecord::Base
  belongs_to :user
end

★2つのmigrationファイルをそれぞれ修正してmigration実行

class DeviseCreateUsers < ActiveRecord::Migration
  def self.up
    create_table(:users) do |t|
      t.trackable
      t.timestamps
    end
  end

  def self.down
    drop_table :users
  end
end

class CreateAuthentications < ActiveRecord::Migration
  def self.up
    create_table :authentications do |t|
      t.integer :user_id
      t.string :provider
      t.string :uid
      t.string :screen_name
      t.string :access_token
      t.string :access_secret
      t.string :bio
      t.string :image_url
      t.string :web_url
      t.string :last_tid
      t.timestamps
    end
  end

  def self.down
    drop_table :authentications
  end
end
$ rake db:migrate
(in /rails/jhoge)
==  DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
   -> 0.0337s
==  DeviseCreateUsers: migrated (0.0340s) =====================================

$ rake db:migrate
==  CreateAuthentications: migrating ==========================================
-- create_table(:authentications)
   -> 0.0156s
==  CreateAuthentications: migrated (0.0157s) =================================


・ログイン処理とルーティングを追加
authenticationsコントローラを作成

class AuthenticationsController < ApplicationController

  def create
    omniauth = request.env['omniauth.auth']
    @authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
    if @authentication
      #sign_in_and_redirect @user, :event => :authentication
      sign_in(:user, @authentication.user)
      set_token_to_session(current_user)
      redirect_to user_root_url

    #elsif current_user          # 既にログインしてるけど、facebookとかの権限も追加するとき
    #  current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'],
    #                                       :oauth_token => omniauth['credentials']['token'],
    #                                       :oauth_token_secret => omniauth['credentials']['secret'])
    #  redirect_to authentications_url
    else                        # 新規ユーザのとき
      data = omniauth['extra']['user_hash']
      @user = User.new
      @user.authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'],
                                  :access_token => omniauth['credentials']['token'],
                                  :access_secret => omniauth['credentials']['secret'],
                                  :screen_name => data['screen_name'],
                                  :bio => data['description'],
                                  :image_url => data['profile_image_url'],
                                  :web_url => data['url'],
                                  :last_tid => nil) # data['id']で取れるけど初期値はnil
      @user.save!

      sign_in(:user, @user)
      redirect_to user_root_url
    end
  end

end


route.rbに以下を追記

  devise_for :users, :controllers => { :sessions => "users/sessions", :authentications=>"authentications" } do
    get '/users/sign_out', :to => 'devise/sessions#destroy', :as => :destroy_user_session
    get '/sessions/new', :to => 'index#index', :as => :new_user_session
  end
  match '/users/auth/:provider/callback' => 'authentications#create'


・ログイン用のリンクをviewの適当な場所に

<%= link_to image_tag('twitter_btn.png'), "/users/auth/twitter" %>


・ログインリンクをクリックしてUsersとAuthenticationsにレコードができてることを確認しておしまい