Rails5でOmniAuthを使ったTwitter連携(応用編)
以前こちらの記事ではOmniAuthを使ってTwitterアカウントでの認証機能をもったWebアプリを作ってみました。今回の記事ではその続きということで、認証後にTwitterのアカウントの情報を使ったWebアプリを作ってみたいと思います。
目標
以下のようなアプリの制作を目標とします。
- / → アカウント情報表示、未ログインで/loginに遷移
- /auth/twitter → Twitter認証実行
- /auth/twitter/callback → Twitter認証後のコールバック先
- /logout → ログアウト処理
- /login → ログインを促す画面表示
ここまでは前回の記事で作成済みです。今回はこれに付け足す形で以下の機能も実装します。
- /items/:id → 指定されたidの記事を表示
- /items/new → 記事の新規作成ページを表示
- /items/create → 記事を作成
- /items/unauth → 記事の公開範囲外のユーザーが遷移される
- 記事は公開範囲を設定できる。フォロワーのみと設定された記事はフォロワー以外には閲覧できない。
記事のモデル作成
以下のコマンドを入力して記事のモデルを作成します。
rails g model Item text:text only_followers:boolean user:references
それぞれ
- text → 記事の本文
- only_followers → 公開範囲をフォロワーのみとするか
- user → userを外部キーに設定
という役割とします。完了後にmigrateしてください。また、ItemとUserを、一対多のリレーションとしたいので、models/user.rbに以下を追記します
has_many :items,dependent: :destroy
記事のコントローラー作成
以下のコマンドを入力して記事を管理するコントローラーを作成します。
rails g controller Items new show create unauthorized
それぞれ
- new → 記事新規作成ページ
- show → 記事閲覧
- create → 記事作成
- unauthorized → 閲覧範囲外の時に遷移するページ
という役割とします。routes.rbに追記してルーティングも行います。なお、generate時に追記されているitems関連のルーティングは消してください。
routes.rb
resources :items
get '/items/unauth',to:'items#unauthorized'
コントローラーの中身も実装していきます。
controllers/items_controller.rb
class ItemsController < ApplicationController
def new
@item = Item.new
end
def create
user = User.find(session[:user_id])
item = user.items.new(user_params)
if item.save
id = item[:id]
redirect_to "/items/"+id.to_s
else
render 'new'
end
end
def show
id = params[:id]
@item = Item.find(id)
end
def unauthorized
end
private
def user_params
params.require(:item).permit(:text, :only_followers)
end
end
至って普通の実装です。newでitemのインスタンスを取得、createで入力情報の保存に成功すればshowに飛び、失敗すれば再度newをrenderし、showで対応するidのitemのインスタンスを取得する、という流れになります。
記事のビュー作成
記事のビューを作っていきます。
views/items/new.html.erb
<%= form_for(@item, url: "/items") do |f| %>
<%= f.label :text %>
<%= f.text_field :text %>
<%= f.label :only_followers %>
<%= f.check_box :only_followers %>
<%= f.submit "submit"%>
<% end %>
views/items/show.html.erb
<%= @item.text %>
views/items/unauthorized.html.erb
ユーザーの設定している公開範囲外です。
スタイルを一切適用していないので質素極まりない見た目になりますが、とりあえず動くものを作りました。
一旦ここまでで、ユーザーがフォームに入力することで、自分のアカウントに紐付いた記事を作成し、公開することが出来るアプリが出来ました。
gemfileの追加
ここからいよいよ、Twitterと連携した機能を作成していきます。まず、gemfileに追記します。
gem 'twitter'
追記後、bundle installしてください。これによりこのgemが導入されます。これを使うことでTwitterAPIを楽に扱うことが出来るようになります。
showメソッド改変
公開範囲を制限する機能をshowメソッドに付け加えます。以下のようにshowメソッドを書き換えてください。
def show
id = params[:id]
@item = Item.find(id)
current_user = User.find(session[:user_id])
owner_user = User.find(@item[:user_id])
redirect_to action:'unauthorized' unless authentication(@item[:only_follower],current_user,owner_user)
end
現在ログインしているユーザーと、記事の所有者のユーザーを取得し、authenticationメソッドに@item[:only_follower]と一緒に渡しています。authenticationの返り値がtrueなら認証成功とし、falseなら失敗としunauthorizedに遷移する実装となっています。
authenticationメソッド実装
authenticationメソッドを実装していきます。
def authentication(only_followers,current_user,owner_user)
if only_followers
require 'twitter'
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.secrets.twitter_api_key
config.consumer_secret = Rails.application.secrets.twitter_api_secret
config.access_token = session[:oauth_token]
config.access_token_secret = session[:oauth_token_secret]
end
current_user_screen_name = current_user[:nickname]
owner_user_screen_name = owner_user[:nickname]
result = client.friendship(current_user_screen_name,owner_user_screen_name)
return result.source.following?
else
return true
end
end
このメソッドはshowメソッド内からの呼び出しにしか使用していないので、private内に実装してください。やっていることは、Twitter::REST::Clientインスタンスの作成(この中で使用しているsession[:oauth_token],session[:oauth_token_secret]については後ほど取得部分を実装します)と、それを使ったログイン中のユーザーと所有者のユーザーの関係性のチェックです。このチェックでログイン中のユーザーが所有者のユーザーのフォロワーであることが確かめられるとtrueが返されます。そもそも公開範囲がフォロワーのみに制限されていない場合もtrueとなります。
oauth_token取得処理追加
先程実装したauthenticationメソッドはoauth_tokenを必要としていますが、まだ取得する処理が書かれていないのでここで実装します。SessionsControllerのcreateメソッドを以下のように変更します。
def create
callback = request.env['omniauth.auth']
user = User.find_or_create_from_auth(callback)
session[:user_id] = user.id
session[:oauth_token] = callback['credentials']['token']
session[:oauth_token_secret] = callback['credentials']['secret']
redirect_to root_path
end
リクエストに含まれるトークンの情報をセッションに保存するようにしました。これでauthenticationメソッドが動作するようになりました。
以上で目標を達成するアプリの製作は完了です!
client.friendshipについて
result = client.friendship(A,B)
上記のとき、result.source.following?はAがBをfollowしてる時にtrueになり、result.source.followed_by?はBがAをfollowしてる時にtrueになります。
余談
このアプリを制作している最中、突然ソースコードを変更していないのにTwitterの認証に失敗することがありました。原因はほんの一秒以下の時計の誤差でした。同じようにソースコードは正しいし時間もほぼあってるのに出来ないと悩んでる方は一度時計合わせをしてみることをおすすめします。
参考
【Ruby on Rails】twitter apiを利用してoauth認証・タイムラインを表示する
Qiita - OmniAuthで認証した後に、tweetしたりfollowしたりする