Rails3 deviseによるtwitter認証

【前回】Rails3 認証エンジン「devise」 - スマートフォンアプリ開発会社のエンジニアブログ

こんにちはRails研究員の[twitter:@gc_locks]です。
今回は前回の続き、deviseにoauth認証を乗せます。

https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
このページを参考にして、ガリガリ実装します。

Gemfile に以下を追加して、bundle install

gem "oa-oauth", :require => "omniauth/oauth"

次に、config/initializers/devise.rb にtwitterのアプリケーションの管理コードを追加。
管理コードは https://dev.twitter.com/apps から取得しましょう。

Devise.setup do |config|
  (略)
  config.omniauth :twitter, "APP_ID", "APP_SECRET"
end

認証をするモデルを作ります。
TwitterAuthというモデルにします。

$ rails g devise TwitterAuth

このままではベーシック認証のモデルになってしまうので、modelとmigrationを変更を加えましょう。
著者の好みで trackableは残します。

app/model/twitter_auth.rb

class TwitterAuth < ActiveRecord::Base
  devise :trackable, :omniauthable
end

db/migrate/..._devise_create_twitter_auths.rb

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

  def self.down
    drop_table :twitter_auths
  end
end

このままではtwitterアカウントの識別子もありませんし、
twitterに侵入して遊べないので、識別子とユーザ側の二つのトークンを加えましょう。

$ rails g migration AddTokenToTwitterAuth uid:integer screen_name:string access_token:string access_secret:string
$ rake db:migrate

これで準備完了したので、routes を見てみましょう。

twitter_auth_omniauth_callback  /twitter_auths/auth/:action/callback(.:format) {:action=>/twitter/, :controller=>"devise/omniauth_callbacks"}

controller が "devise/omniauth_callbacks" だと不便なので、routesを書き換えましょう
config/routes.rb

devise_for :twitter_auths, :controllers => {:omniauth_callbacks => "omniauth_callbacks"}

$ rake routes

twitter_auth_omniauth_callback  /twitter_auths/auth/:action/callback(.:format) {:action=>/twitter/, :controller=>"omniauth_callbacks"}

これで、twitter_auth_omniauth_authorize_path にアクセスすると twitterの認証画面にリダイレクトされます。
そこでの認証に成功したら、 :controller => "omniauth_callbacks", :action => "twitter" にコールバックされます。

$ rails g controller OmniauthCallbacks twitter

app/controller/omniauth_callbacks_controller.rb

class OmniauthCallbacksController < ApplicationController
  def twitter
    # You need to implement the method below in your model
    @user = TwitterAuth.find_for_twitter_auth_oauth(env["omniauth.auth"], nil)

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

app/model/twitter_auth.rb

class TwitterAuth < ActiveRecord::Base
  devise :trackable, :omniauthable

  def self.find_for_twitter_auth_oauth(access_token, signed_in_resource=nil)
    data = access_token['extra']['user_hash']
    if user = TwitterAuth.find_by_uid(access_token['uid'].to_i)
      user
    else # Create a user with a stub password. 
      TwitterAuth.create!({
                           :uid => access_token['uid'].to_i,
                           :screen_name => data['screen_name'],
                           :access_token => access_token['credentials']['token'],
                           :access_secret => access_token['credentials']['secret']})
    end
  end
end

これでサーバを起動して、http://localhost:3000/twitter_auths/auth/twitter にアクセスしてユーザが追加出来れば成功です。

次回はここから、twitterに侵入して遊びましょう。
gcでした。