はじめに
10年ほど前にOmniAuthでTwitterログインをするという機能をいくつかのシステムで実装したのですが、久しぶりに同じことをやってみようとすると時間がかかってしまったので、今回実施した内容をまとめておくことにしました。
以下のバージョンで動作を確認しています。deviseとは連携しないでOmniAuth単体でユーザー認証をします。
- Ruby 3.1.2
- Rails 7.0.4
- OmniAuth 2.1.0
- OmniAuth::Twitter2 0.1.0
ライブラリのインストール
TwitterのAPIがv2になっていたので、それに対応したStrategyのライブラリを利用します。
gem "omniauth-twitter2"
gem "omniauth-rails_csrf_protection"
% bundle
Client ID, Client Secretの取得と設定
Twitter Developer Portalでアプリを登録します。
アプリを登録した後でUser authentication settings
からClient ID
とClient Secret
を作成します。
Callback URI
にはhttp://localhost:3000/auth/twitter2/callback
を設定します。
EDITOR=vi rails credentials:edit
を実行してClient ID
とClient Secret
を保存します。
twitter:
client_id: ********
client_secret: ****************
omniauth.rb
を作成します。必要な権限に応じてscope
を変更します。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter2,
Rails.application.credentials.twitter[:client_id],
Rails.application.credentials.twitter[:client_secret],
callback_path: "/auth/twitter2/callback",
scope: "tweet.read users.read"
OmniAuth.config.on_failure =
Proc.new { |env| OmniAuth::FailureEndpoint.new(env).redirect_to_failure }
end
ユーザーモデルとセッションコントローラーの作成
ユーザーモデル
以下の例ではprovider
とuid
とname
のみを格納していますが、作成するアプリに応じてデータ項目を増やすことになります。
% rails g model User provider:string uid:string name:string
% rake db:migrate
class User < ApplicationRecord
def self.find_or_create_from_auth(auth)
provider = auth[:provider]
uid = auth[:uid]
self.find_or_create_by(provider: provider, uid: uid) do |user|
user.name = auth[:info][:name]
end
end
end
セッションコントローラー
% rails g controller sessions
class SessionsController < ApplicationController
def create
user = User.find_or_create_from_auth(request.env["omniauth.auth"])
session[:user_id] = user.uid
redirect_to request.env["omniauth.origin"] || root_url
end
def destroy
reset_session
redirect_to root_url, status: :see_other
end
def failure
redirect_to root_url
end
end
ルーティングを追加します。
get "/auth/:provider/callback", to: "sessions#create"
get "/auth/failure", to: "sessions#failure"
delete "/sign_out", to: "sessions#destroy"
アプリ全体で利用するメソッドの作成
application_controller.rb
にメソッドを追加します。Deviseのメソッド名と同じようにしてみました。
class ApplicationController < ActionController::Base
helper_method :current_user, :user_signed_in?
private
def authenticate_user!
redirect_to root_path unless session[:user_id]
end
def current_user
return unless session[:user_id]
@current_user ||= User.find_by(uid: session[:user_id])
end
def user_signed_in?
!!session[:user_id]
end
end
動作確認
ログインとログアウトの状態に応じて表示するボタンを切り替える例です。
<% if user_signed_in? %>
<%= link_to "Sign out", "/sign_out",
data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
<% else %>
<%= button_to "Sign in", "/auth/twitter2", method: :post,
data: { turbo: false } %>
<% end %>
before_action
にauthenticate_user!
を指定して、ログインしているときのみ表示する例です。
class ContentsController < ApplicationController
before_action :authenticate_user!
end
おわりに
Rails 7にTwitterログイン機能を搭載することができました。私はこれまでOmniAuthとdeviseを組み合わせた構成にしていたのですが、OmniAuthのみでシンプルな構成もよさそうなので、次に開発するアプリはこの構成でいきたいと思っています。
参考