簡単なTwitterログインを実装したので、方法をまとめていきます。
ログイン方法はTwitter連携だけなので、devise等は利用していません。
仕様は以下の通りです。
- ログイン方法はTwitterのみ(メールアドレスでのログインは出来ない)
- ログインボタンをクリックすると、Twitterの認証画面に遷移する
- 初めてログインする場合にはusersテーブルにレコードを作成、2回目以降の場合には既存のデータを読み込む
- ログイン後は、sessionが開始する
アプリはすでにあって、トップページ等の設定は出来ている前提で話を進めていきます。
##API Key、API Secret Keyを取得
Twitterと連携するためには、TwitterのAPIキーとAPIシークレットキーが必要です。以下の手順で取得しましょう。
###アカウント取得
まずは以下の記事等を参考に、開発者アカウントを取得しましょう。
Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報
2019年12月29日に申請を出したところ、即時承認されました。待ち時間0秒でした。
###アプリ作成
アカウント取得、ログイン後
https://developer.twitter.com/en/apps/create
よりアプリを作成します。
Callback URLsには
http:localhost:3000/auth/twitter/callback
http:0.0.0.0:3000/auth/twitter/callback
http:<本番環境のドメイン>/auth/twitter/callback
https:<本番環境のドメイン>/auth/twitter/callback
の4つを指定します。
最低2つ設定されていないと動かない仕様になっているようです。
他は基本的にはそれっぽいことを書いておけばOKです。
また設定は後から自由に変更できます。
###キーを取得
アプリ一覧画面
https://developer.twitter.com/en/apps
から作成したアプリ右の「Details」ボタンをクリック→「Keys and tokens」タブからAPIキーとAPIシークレットキーが取得出来ます。
##必要なgemのセットアップ
gemのインストールと、各種設定を行います。
###dotenv-rails
APIキーとAPIシークレットキーを環境変数に保存するためにdotenv-rails
を導入します。
gem 'dotenv-rails'
$ bundle install
プロジェクトルートに.env
ファイルを作成し、上記で取得した各種キーを書き込みます。
TWITTER_API_KEY = hogehoge
TWITTER_API_SECRET = foobarbaz
###omniauth-twitter
Twitter連携のためのgemomniauth-twitter
を導入します。
gem 'omniauth-twitter'
$ bundle install
config/initializers/omniauth.rb
を作成し、APIキーとAPIシークレットキーを書き込みます。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET']
end
##モデル、データベースを作成
TwitterのID、ニックネーム、ユーザー名、画像パスを保存するためのデータベースと関連するモデルを作成します。
上記は全てomniauth-twitterで取得できる値です。その他の値についてはomniauth-twitterのREADMEをご覧ください。
$ rails g model User uid:string nickname:string name:string image:string
$ rails db:migrate
##セッション管理用のヘルパーメソッドを作成
module SessionsHelper
# 渡されたユーザーでログインする
def log_in(user)
session[:uid] = user.uid
end
# 現在ログイン中のユーザーを返す (いる場合)
def current_user
if session[:uid]
@current_user ||= User.find_by(uid: session[:uid])
end
end
# ユーザーがログインしていればtrue、その他ならfalseを返す
def logged_in?
!current_user.nil?
end
# 現在のユーザーをログアウトする
def log_out
session.delete(:uid)
@current_user = nil
end
end
##ログイン、ログアウト処理を実装
$ rails g controller Sessions
class SessionsController < ApplicationController
def create
unless request.env['omniauth.auth'][:uid]
flash[:danger] = '連携に失敗しました'
redirect_to root_url
end
user_data = request.env['omniauth.auth']
user = User.find_by(uid: user_data[:uid])
if user
log_in user
flash[:success] = 'ログインしました'
redirect_to root_url
else
new_user = User.new(
uid: user_data[:uid],
nickname: user_data[:info][:nickname],
name: user_data[:info][:name],
image: user_data[:info][:image],
)
if new_user.save
log_in new_user
flash[:success] = 'ユーザー登録成功'
else
flash[:danger] = '予期せぬエラーが発生しました'
end
redirect_to root_url
end
end
def destroy
log_out if logged_in?
flash[:success] = 'ログアウトしました'
redirect_to root_url
end
end
##ログイン、ログアウト用のリンクを追加
<header class="header">
.
.
<% if logged_in? %>
<%= link_to 'ログアウト', logout_path %>
<% else %>
<a href="/auth/twitter">ログイン</a>
<% end %>
.
.
</header>
##ルーティングを設定
Rails.application.routes.draw do
.
.
get '/auth/:provider/callback', to: 'sessions#create'
get '/logout', to: 'sessions#destroy'
end
omniauth-twitter
の仕様で、/auth/twitter/callback
が/auth/:provider/callback
に変換されます。
ログイン状態の振る舞いのテスト
今回、session[:uid]
にユーザーidを代入形でログイン処理を実装しました。
RSpecでログイン状態のユーザーを作る時にもsession[:uid] = user.uid
と書きたいところですが、
RSpecのfeature spec及びsystem specではデフォルトでsession
メソッドが使えません。
そこでrack_session_accessをインストールして使えるようにします。
group :test do
# 省略
gem 'rack_session_access'
end
$ bundle install
Rails.application.configure do
# 省略
config.middleware.use RackSessionAccess::Middleware
RSpec.configure do |config|
# 省略
require 'rack_session_access/capybara'
end
it 'ログイン' do
visit root_path
expect(page).to_not have_content 'マイページ' # ログイン前はマイページという表示が無い
page.set_rack_session(uid: user.uid) # ログイン
visit root_path
expect(page).to have_content 'マイページ' # マイページと表示されている
end
##参考