#はじめに
Twitter認証をDeviseなしで実装しました。
備忘録も兼ねて記事にしたいと思います。
【実行環境】
・ Rails 6.0.0
・ Ruby 2.6.5
【使用gem】
gem 'omniauth'
gem 'omniauth-twitter'
gem 'dotenv-rails'
#Twitter API のアカウント取得
まずは開発者用のアカウントを取得します。
Twitter API 登録
私はこちらをの記事を参考にさせていただきました。
申請の際に英文を書く必要がありますが、私はDeepLで翻訳したものを記入しました。
私の時は2020年9月2日に申請を行いましたが即認証されました。
私の記入例です。使用目的が限られていたのでこの二つを記入しました。
【TwitterAPIの利用目的】
私は今、卒業制作用のwebアプリを制作しています。プロフィールを表示し、ユーザーがスケジュールを記入しておけば日程の合う人が可視化され、リプライを送ることができるアプリを制作する予定です。Twitter APIを使用してTwitterログインとフォロワーの取得、リプライを送ることができる機能を実装したいと考えています。
英訳
I'm currently working on a web app for my graduation project.
I will create an app that displays profiles and allows users to fill out a schedule to visualize and send replies to people who match their schedule.
I would like to implement the ability to use the Twitter API to get Twitter login and followers and send replies.
【アプリはツイート、リツイート、いいね、フォロー、ダイレクトメッセージ機能を使用しますか?】
アプリでスケジュールの合致した相手を検索し、検索結果に応じてメッセージを送る相手を選択してリプライを送りたいと思っています。検索条件に相互フォローであることを加えたいと思っています。
英訳
I'd like to search for people who match my schedule on the app and send a reply by selecting the person I want to message based on the search results.
I'd like to add that one of the search criteria is mutual following.
#アプリ作成
アカウント取得後はこちらにログインしアプリの設定を行います。
アプリの名前などを登録しますが一番大事なのはcallbackURL(登録後に遷移するページのURL)の設定です。
401エラーがでたら真っ先にここを疑いましょう。
アプリ詳細画面の[Authentication settings]よりeditをクリック。
CALLBACK URLSにローカル環境と本番環境のURLを2つずつ指定します。
ここで指定するURLはTwitter認証後に飛びたいURLになります。
私の場合はルートパスに飛ばしたかったのでこのように記述してあります。
http://localhost:3000/auth/twitter/callback
http://127.0.0.1:3000/auth/twitter/callback
http:<本番環境のドメイン>/auth/twitter/callback
https:<本番環境のドメイン>/auth/twitter/callback
私はAWSのEC2をドメイン取得せずに利用していたので
http:12.315.678.910/auth/twitter/callback (本番環境のドメインがなくIPアドレスを記述した例)
このようにIPアドレスを直で入力していました。
#Twitter連携のgemを導入
Twitter連携をするためにomniauthとomniauth-twitterのgemをインストールします。
これらを使うことでTwitterアカウントの情報を使ってユーザー登録やログインなどができるようになります。
この二つについての説明はこちらの記事が詳しいです
gem 'omniauth'
gem 'omniauth-twitter'
% bundle install
#TwitterのAPIキーとTwitterAPIシークレットキーを環境変数として定義する
gemのインストールが終わったらTwitterのAPIキーとTwitterAPIシークレットキーを取得し、環境変数に保存します。
dotenv-railsを導入することでrailsでも簡単に環境変数を扱うことができるようになります。
gem 'dotenv-rails'
% bundle install
アプリ一覧画面からアプリ詳細画面へと飛び、「Keys and tokens」タブからTwitterAPIキーとTwitterAPIシークレットキーを取得してきます。
これらは公開したくないデータなのでアプリケーションディレクトリのルートディレクトリ(appやdbやGemfileがあるディレクトリ)に「.env」を作成し、環境変数として定義します。
TWITTER_API_KEY = '取得してきた自分のTwitterAPIキー'
TWITTER_API_SECRET = '取得してきた自分のTwitterAPIシークレットキー'
また、せっかく設置した環境変数をgitに上げないためにも、.gitignoreに記述しpushの対象から外しておきます。記述場所は最下部でいいです。
##~省略~
/.env
マージしてmasuterブランチにpullした後で、開発環境でログインができなくなった!
という場合には環境変数を定義した.envファイルがmasuterブランチになかったのが原因である場合もあるのでエラーが起きたら疑ってみるのもいいでしょう。私がそうでした。
その場合はmasterブランチでまた同様に.envファイルを作成し環境変数を設置すれば大丈夫です。
#認証に使用するTwitterAPIキーとTwitterAPIシークレットキーの宣言
次に「config/initializers/omniauth.rb」を作成し、下記のように記入します。
説明のため一部の記述だけに留めています。後でまた戻ってきます。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET']
end
2行目でTwitter認証に使う、環境変数に定義したTwitterAPIキーとTwitterAPIシークレットキーを宣言しています。
#モデルとデータベースの作成
次にuserモデルとテーブルを作成します。
指定しているカラムはTwitterのID、アカウント名(リプライで使う@の後ろにつく英数字)、ユーザー名、画像パスを保存するためのものです。
他のSNS認証やDeviseと併用する場合はproviderカラムも作成する必要があるそうですが、Twitter認証のみなのでここでは作成しません。
これらは全てomniauth-twitterで取得できる値です。その他の値など詳しくはomniauth-twitterのREADMEをどうぞ。
% rails g model user uid:string nickname:string name:string image:string
% rails db:migrate
class User < ApplicationRecord
def self.find_or_create_from_auth_hash(auth_hash)
uid = auth_hash[:uid]
nickname = auth_hash[:info][:nickname]
name = auth_hash[:info][:name]
image = auth_hash[:info][:image]
# find_or_create_by()は()の中の条件のものが見つければ取得し、なければ新しく作成するというメソッド
find_or_create_by(uid: uid) do |user|
user.uid = uid
user.nickname = nickname
user.name = name
user.image = image
end
end
end
#コントローラーの作成
% rails g controller Sessions
#セッション管理用のヘルパーメソッド作成
deviseを使用していないためログイン、ログアウトなどのメソッドを自作する必要があります。
module SessionsHelper
# 渡されたユーザーでログインする
def log_in(user)
session[:uid] = user.uid
end
# 現在ログイン中のユーザーを返す (いる場合)
def current_user
@current_user ||= User.find_by(uid: session[:uid]) if session[:uid]
end
# 受け取ったユーザーがログイン中のユーザーと一致すればtrueを返す
def current_user?(user)
user == current_user
end
# ユーザーがログインしていればtrue、その他ならfalseを返す
def logged_in?
!current_user.nil?
end
# 現在のユーザーをログアウトする
def log_out
session.delete(:uid)
@current_user = nil
end
end
作成したヘルパーメソッドを全てのページで使えるようにするために、Applicationコントローラから読み込みを行います。
class ApplicationController < ActionController::Base
include SessionsHelper
end
#ログイン、ログアウト、認証に失敗した時のアクションを記述。
class SessionsController < ApplicationController
def create
raise 'request.env[omniauth.auth]がありません' if auth_params.nil?
user = User.find_or_create_from_auth_hash(auth_params)
if user
log_in(user)
flash[:notice] = 'ログインしました'
redirect_to root_path
else
flash[:notice] = '失敗しました'
redirect_to root_path
end
end
def destroy
log_out if logged_in?
flash[:notice] = 'ログアウトしました'
redirect_to root_path
end
# callbackに失敗したときに呼ばれるアクション
def failure
flash[:notice] = 'キャンセルしました'
redirect_to root_path
end
private
# request.env['omniauth.auth']はTwitter認証で得た情報を格納するもの
def auth_params
request.env['omniauth.auth']
end
end
#認証に失敗した時のコールバックの記述
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET'], callback_url: ENV['TWITTER_API_SECRET'] ##コールバックURL明示
OmniAuth.config.on_failure = Proc.new { |env|
OmniAuth::FailureEndpoint.new(env).redirect_to_failure ##コールバックに失敗した時のアクション設定
}
end
先ほど記述したファイルに付け足しを行なっています。
callbackが失敗した時、どこにリダイレクトすればいいのかはconfig/initializers/omniauth.rbで設定できます。
callback_url: ENV['TWITTER_API_SECRET']はコールバックURLを明示化するための記述です。
ローカル環境では認証できていたのに、本番環境ではコールバックが上手くいかなかったということのないように記述しています。
開発環境と本番環境で指定するコールバックURLが異なるため、URL部分は環境変数で代入しています。
TWITTER_API_KEY = '自分のTwitterAPIキー'
TWITTER_API_SECRET = '自分のTwitterAPIシークレットキー'
TWITTER_CALLBACK: 'http://localhost:3000/auth/twitter/callback' ##開発環境に設置する
TWITTER_API_KEY = '自分のTwitterAPIキー'
TWITTER_API_SECRET = '自分のTwitterAPIシークレットキー'
TWITTER_CALLBACK=`http:<本番環境のドメイン>/auth/twitter/callback` ##本番環境に設置する
デプロイできたら本番環境側にも環境変数を設置する必要があります。
例えば本番環境でHerokuを使う場合はこのようにして設置します。
% heroku config:add TWITTER_API_KEY = '自分のTwitterAPIキー'
% heroku config:add TWITTER_API_SECRET = '自分のTwitterAPIシークレットキー'
% heroku config:add TWITTER_CALLBACK=`http:<本番環境のドメイン>/auth/twitter/callback`
#ビューをログインとログアウトで表示を変える
ログインとログアウト用のリンクも記述しています。
<header>
.
.
<% if logged_in? %>
<%= link_to 'ログアウト', logout_path, class: "logout" %>
<% else %>
<%= link_to 'ログイン', "/auth/twitter", class: "login" %>
<% end %>
.
.
</header>
#ログイン状況の確認
ログインしていないユーザーをトップページへリダイレクトさせるアクションです。
application_controllerに追加しているので全てのコントローラーで利用できます。
class ApplicationController < ActionController::Base
include SessionsHelper
private
# ログイン済みユーザーかどうか確認
def logged_in_user
redirect_to root_path unless logged_in?
end
end
またbefore_actionを使用すると、コントローラで定義されたアクションが実行される前に、共通の処理を行うことができるようになります。
class 好みのコントローラ名 < ApplicationController
before_action :処理させたいメソッド名
before_action :logged_in_user, only:[:edit, :update]
#ルーティングを設定
最後に各ルーティングを設定して終わりです。
get '/auth/:provider/callback', to: 'sessions#create' #ログイン認証
get '/logout', to: 'sessions#destroy' #ログアウト用
get "/auth/failure", to: "sessions#failure" #認証失敗時用
#終わり
自分が次また同じように設定する時、忘れないように書きました。
間違いや改善点などあれば言ってもらえればと思います。
外部APIを使用しているのでTwitter認証の結合テストコードを書くときにもすごい苦労したのですがそれもまた余裕のあるときに書きたいと思います。
結合テストコードを記述した時の記事を書きました
参考にさせてもらった記事
https://qiita.com/d0ne1s/items/7a96b835280f55bb5234
https://qiita.com/keiya01/items/c96a0393c76f5560ee41
https://qiita.com/d0ne1s/items/7c4d2be3f53e34a9dec7
https://qiita.com/ngron/items/88f04a2d864f9f33b389
https://www.wantedly.com/companies/clueit/post_articles/43708