9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ruby on RailsAdvent Calendar 2022

Day 4

Rails 7にOmniAuthでTwitterログイン機能を搭載

Posted at

はじめに

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のライブラリを利用します。

Gemfile
gem "omniauth-twitter2"
gem "omniauth-rails_csrf_protection"
% bundle

Client ID, Client Secretの取得と設定

Twitter Developer Portalでアプリを登録します。

アプリを登録した後でUser authentication settingsからClient IDClient Secretを作成します。

Callback URIにはhttp://localhost:3000/auth/twitter2/callbackを設定します。

EDITOR=vi rails credentials:editを実行してClient IDClient Secretを保存します。

twitter:
  client_id: ********
  client_secret: ****************

omniauth.rbを作成します。必要な権限に応じてscopeを変更します。

config/initializers/omniauth.rb
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

ユーザーモデルとセッションコントローラーの作成

ユーザーモデル

以下の例ではprovideruidnameのみを格納していますが、作成するアプリに応じてデータ項目を増やすことになります。

% rails g model User provider:string uid:string name:string
% rake db:migrate
app/models/User.rb
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
app/controllers/sessions_controller.rb
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

ルーティングを追加します。

config/routes.rb
get "/auth/:provider/callback", to: "sessions#create"
get "/auth/failure", to: "sessions#failure"
delete "/sign_out", to: "sessions#destroy"

アプリ全体で利用するメソッドの作成

application_controller.rbにメソッドを追加します。Deviseのメソッド名と同じようにしてみました。

app/controllers/application_controller.rb
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

動作確認

ログインとログアウトの状態に応じて表示するボタンを切り替える例です。

app/views/layouts/application.html.erb
<% 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_actionauthenticate_user!を指定して、ログインしているときのみ表示する例です。

app/controllers/contents_controller.rb
class ContentsController < ApplicationController
  before_action :authenticate_user!
end

おわりに

Rails 7にTwitterログイン機能を搭載することができました。私はこれまでOmniAuthとdeviseを組み合わせた構成にしていたのですが、OmniAuthのみでシンプルな構成もよさそうなので、次に開発するアプリはこの構成でいきたいと思っています。

参考

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?