(メモ) Rails+OmniAuthによるTwitterログイン
Ruby/Rails向けの認証連携フレームワークの定番らしいOmniAuthを使って、OAuth経由でTwitterに投稿するサンプルRailsアプリを作ってみました。
参考にしたサイト:
「簡単なOmniAuth」に詳細な使い方が説明されていますが、現在のOmniAuth 1.1と合っていない箇所があるので、適宜公式ドキュメントを参照しています。
動作環境:
- Ubuntu 12.10 Desktop (32ビット版) on VMware Player
- Ruby 1.9.3-p362
- Rails 3.2.10
- omniauth 1.1.1
- omniauth-twitter 0.0.14
- twitter 4.4.2 (Twitter APIのRubyバインディングの1つ)
Twitterのアプリケーション登録
まず準備作業として、Twitterの開発者サイト (https://dev.twitter.com/) で新規のアプリケーションを登録します。開発者サイトの構成は割と良く変わるようですが、この記事を書いた時点では、ログインした状態で右上の自分のアイコンにマウスオーバー→My Applicationで行けるようです。
My applicationsから "Create a new application" を選択し、必要な情報を入力します。ここで重要なのは "Callback URL" で、TwitterのWebサイト上でユーザ認証した後に、ここに入れたURLにリダイレクト (コールバック) されることになります。今回は、元ネタのASCIIcasts/RailsCastsの記述に合わせて、http://127.0.0.1:3000/auth/twitter/callback とします。
あと、本筋ではありませんが、ここで作成したアプリケーションからTwitterにメッセージを投稿するために、Access TypeをRead onlyからRead and Writeに変える必要があります。この設定は新規アプリケーション作成時にはできなくて、一旦アプリを作成してからSettingsで変更する必要があるようです。
アプリケーションのページに表示されるOAuthのConsumer key・Consumer secretの内容を後で使います。
ライブラリを使うための準備
RailsアプリケーションからOmniAuthおよびTwitterライブラリを使うために、Gemfileへの情報追加とBundlerの実行を行います。
rails newコマンドで作成したRailsアプリ (ここではtwitter-omniauth-testとします) のルート直下にあるGemfileファイルに以下の内容を追加した後で、bundle installを実行します。
twitter-omniauth-test/Gemfileの追加内容:
.... # 以下の2行を追加 gem 'omniauth-twitter' gem 'twitter'
アプリケーションの動作手順
ここで、OmniAuth+omniauth-twitterを使ったアプリケーションの動作手順 (ページ遷移) について整理しておきます。
- まず、ユーザはアプリケーションのトップ画面をブラウザで開きます。この中にある "Sign in with Twitter" のリンクをクリックすると、omniauth-twitterの制御下に移ります。このURLは /auth/twitter となります。
- omniauth-twitterは、OAuthのアクセストークンを取得するため、Twitterのサイトにリダイレクトします。
- Twitterサイトとユーザの間で認証処理を行います。すでにユーザがTwitterにログイン済みである場合には、ユーザは何もせずに4.に進むことになります。
- TwitterサイトからOAuthコールバックURL (Twitter開発者サイトで入力したもの) にリダイレクトします。
- OAuthコールバックURLに対応するコントローラの中で、認証に成功したユーザについてのセッションを作成します。具体的には、Twitterから発行されたアクセストークンの格納を行います。
- OAuthコールバックURLからアプリケーションのトップ画面にリダイレクトします。この状態では、ユーザはすでにログイン済みなので、"Sign in with Twitter" の代わりに "Sign out" のリンクが現れます。
この動作手順のうち、1、5、6の部分はアプリケーション内で記述する必要があり、それに加えてomniauth-twitterを使うための記述が必要になります。
OmniAuth初期化スクリプトの作成
omniauth-twitterに対してOAuthのConsumer Key/Secretをセットするための初期化スクリプトを追加します。
twitter-omniauth-test/initializers/omniauth.rb:
Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, CONSUMER_KEY, CONSUMER_SECRET end
CONSUMER_KEY/SECRETには、先ほど取得したConsumer key/secretの内容を入れます。
アプリケーションのトップ画面
ここではビューの中で、ユーザのログイン状態に応じて、ログイン/ログアウト用のリンクを生成します。
コントローラの中で、ユーザがログイン状態であるかどうかを判別するためのヘルパーメソッドを定義します。今回はRailsのセッション機能をそのまま使って、セッション情報にOAuthのアクセストークンが含まれていればログイン状態と判断しています。
twitter-omniauth-test/app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base protect_from_forgery helper_method :signed_in? private def signed_in? true if session[:oauth_token] end end
session[:auth_token] の格納については後述します。
ビューでは、先ほど定義した signed_in? メソッドを用いて表示するリンクを切り替えます。
twitter-omniauth-test/app/views/layouts/application_controller.html.erb:
<body> <div id="container"> <div id="user_nav"> <% if signed_in? %> Welcome <%= session[:username] %>! <%= link_to "Sign Out", "/signout" %> <% else %> <%= link_to "Sign in with Twitter", "/auth/twitter" %> <% end %> </div> <%= yield %> </div> </body>
ここで、未ログイン状態のユーザが "Sign in with Twitter" のリンクをクリックすると、omniauth-twitterを介してTwitterサイトにリダイレクトされることになります。
セッションの作成
Twitterサイトでの認証に成功すると、http://localhost:3000/auth/twitter (Twitter開発者サイトで指定したコールバックURL) にリダイレクトされます。
このURLをコントローラsessionsのアクションcreateで処理するために、ルーティングの設定を追加します。
twitter-omniauth-test/config/routes.rb:
TwitterOmniauthTest::Application.routes.draw do root :to => "tweet#input" get "tweet/input" post "tweet/update" match "/auth/:provider/callback" => "sessions#create" match "/signout" => "sessions#destroy" end
OAuthを用いた認証に成功すると、OmniAuthにより request.env["omniauth.auth"] にアクセストークンなどの情報がセットされるので、sesssions#createではこれらの情報を取得します。参照元のRailsCastではモデルオブジェクトに情報をセットしていますが、ここでは安直にsessionに入れています。
(RailsのデフォルトではCookieに入ってクライアント側に渡ることになりますが、本来はサーバ側でDBなどに格納するのが正しいはずです)
class SessionsController < ApplicationController def create auth = request.env["omniauth.auth"] session[:oauth_token] = auth.credentials.token session[:oauth_token_secret] = auth.credentials.secret session[:username] = auth.extra.access_token.params[:screen_name] redirect_to root_url, :notice => "Signed in!" end def destroy session[:oauth_token] = nil session[:oauth_token_secret] = nil session[:username] = nil redirect_to root_url, :notice => "Signed out!" end end
ツイート送信
OAuthに関する処理はこれで完了ですが、取得したアクセストークンを使ってTwitterにアクセスできることを確認します。
アプリケーションのトップ画面に以下のようなフォームを作成して、フォームに入力したメッセージをTwitterに送信します。
twitter-omniauth-test/app/views/tweet/input.html.erb:
<h1>Twitter Sample Application</h1> <p>Enter a message:</p> <%= form_tag({:action => "update"}, {:method => "post"}) do %> <%= text_field_tag(:message) %> <%= submit_tag("Submit") %> <% end %>
tweet#update のコントローラでは、先ほど取得したアクセストークンを使用してツイートを投稿します。
twitter-omniauth-test/app/controllers/tweet_controller.rb:
class TweetController < ApplicationController def input end def update if signed_in? client = Twitter::Client.new( :oauth_token => session[:oauth_token], :oauth_token_secret => session[:oauth_token_secret] ) client.update(params[:message]) @result = :success else @result = :not_signed_in end end end