やなぎにっき

学んだことの記録

Ruby on RailsでTwitter認証だけのログイン機能を実装する

既にいろんなTwitter認証を実装する記事がありますが、2021年に行われたomniouth2系の仕様変更で詰まったので自分なりにまとめておきます。

環境

Railsのバージョン : Rails 6.1.4.1

Rubyのバージョン : v3.0.2

使用したgem

gem 'dotenv-rails'
gem 'meta-tags'
gem 'omniauth-rails_csrf_protection'
gem 'omniauth-twitter'

仕様

  • Twitter認証のみでログインする(devise等は使わない)

参考記事

Railsアプリに簡単なTwitterログインを実装する - Qiita

Ruby on RailsでTwitter認証機能を実装してみる - Reasonable Code

第9章 発展的なログイン機構 - Railsチュートリアル

TwitterAPIの取得

Twitterと連携するためには、TwitterAPIキーとAPIシークレットキーが必要です。

申請方法などはこの記事では省略します。比較的最新の記事を参照することをおすすめします。

2021/9に申請を出したところ、すぐに承認メールがきました。用途や申請内容によって待ち時間が変わってくるようです。

TwitterAPIキーとAPIシークレットキーを設定

APIの認証情報を登録する前に、GitHunにアクセスキーを公開させないために 'dotenv-rails' をインストールします。

github.com

gem 'dotenv-rails'

本番環境でherokuなどを使う場合は、Gemfileはgroup :development, :testの中に入れます。 別途heroku側で環境変数を設定する必要があります。

.env ファイルを作成し、取得した各種キーを設定します。

TWITTER_API_KEY = 'hogehgoe'
TWITTER_API_SECRET = 'fugafuga'

omniauth-twitter導入

omniauth-twitter を導入します。

github.com

gem 'omniauth-twitter'

config/initializers/omniauth.rbを作成し、APIキーとAPIシークレットキーを設定します。

config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET']
end

ログイン画面にリダイレクトできるか確認

ここまで設定したら、Twitterログイン画面に遷移できるか確認をします。

http://localhost:3000/auth/twitter に遷移できることを確認します。 直接URLを叩くか、ビューにリンクを作成して画面上から遷移してください。

<%= link_to 'ログイン', "/auth/twitter", method: :get %>

omniauth のバージョンが2系の場合*1rails serverでサーバーを起動して直接URLを叩いてみるとエラーになり、No route matches [GET] "/auth/twitter" というエラー画面に遷移するはずです。

というのも、OmniAuth1系までは認可画面へリダイレクトする際、デフォルトでGET/POSTどちらも有効でしたが、CSRF脆弱性の対応に伴い2.0からはPOSTのみに変更になったためです。

参考:

github.com

zenn.dev

2.0 のメジャーバージョンのリリースが2021年1月12日にあったため、それ以前の記事ではGETで実装できているようです。そのことに気づかず自分はハマりました……。

この変更点の対応として、Cookpad社が公開しているCSRF脆弱性対応用のGemを追加します。

gem 'omniauth-rails_csrf_protection'`

認可画面URLへのリダイレクトさせるリンクへのアクセス方法をGETからPOSTに変更します。

home#index などのビューに

<%= link_to 'ログイン', "/auth/twitter", method: :post %>

を記載し、画面上から確認します。

画面からログインボタンをクリックすると見たことがあるであろう画面が出てくるはずです。 (ここでまたエラー画面が出る場合は認証キーの設定が間違えているなどの他の原因が考えられます)

f:id:yana_g:20211206154222p:plain

Twitter認証後の処理を実装する

認可画面に遷移できたので、今度は「連携アプリを認証」ボタンを押した後の処理を作成していきます。 認証ボタンを押した後はTwitter側がコールバックURLに遷移します。

最終的にはログインしたユーザーをサーバーに保存したりログイン状態にしたりしますが、その前に一度ログイン後に自分のアプリの画面に遷移することだけを確認していきます。

コールバックするまでを確認する

「連携アプリを認証」ボタンを選択したときのアクションを作成します。

SessionsController を作成します。

$ rails generate controller Sessions
class SessionsController < ApplicationController
  def create
    user_data = request.env['omniauth.auth']
    session[:nickname] = user_data[:info][:nickname]
    redirect_to root_path, notice: 'ログインしました'
  end
end

request.env['omniauth.auth'] にはTwitter認証したユーザーの情報が格納されています。

取得できる情報はomniauth-twitterのREADMEを参照してください。

github.com

今回は試しにnicknameを参照します。

nicknameがちゃんと取得できているか、ビューファイルから確認してみます。

app/views/homes/index.html.erb

<h1>Twitter連携</h1>
<% if flash[:notice] %>
  <p><%= flash[:notice] %></p>
<% end %>
<% if session[:nickname] %>
  <p><%= "#{session[:nickname]}さん、こんにちは" %></p>
<% end %>

config/routes.rb

Rails.application.routes.draw do
  root 'homes#index'
  get '/homes', to: 'homes#index'
  get '/auth/:provider/callback', to: 'sessions#create'
end

これでログインしたユーザーのidが表示されたhome画面に遷移することができました!

現在はただログインしたユーザーの情報を取得しているだけなので、今度はUserテーブルに登録できるようにしていきます。

Userモデルを作成する

$ rails g model User uid:string nickname:string name:string image:string
$ rails db:migrate

ユーザー登録ができるようにする

セッション用のメソッドはapp/helpers/sessions_helper.rb に記載していきます。

Helperメソッドはデフォルトでビュー内で使用することができますが、コントローラー内では使用することができません。 コントローラーでも使えるようにします。

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  include SessionsHelper
end

セッション用のヘルパーメソッドを作成します。 (Railsチュートリアルの8章を参考にしています)

app/helpers/sessions_helper.rb

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

ログイン、ログアウト処理実装

app/controllers/sessions_controllers.rb

classSessionsController< ApplicationController
  def create
user= User.find_or_create_from_auth_hash(auth_params)
    ifuser
log_in(user)
      flash[:notice] = 'ログインしました'
    else
      flash[:notice] = '失敗しました'
    end
    redirect_to root_path
  end

  def destroy
    log_out if logged_in?
    flash[:notice] = 'ログアウトしました'
    redirect_to root_path
  end

  def failure
    flash[:notice] = 'キャンセルしました'
    redirect_to root_path
  end

  private
  def auth_params
    request.env['omniauth.auth']
  end
end

app/models/user.rb

classUser< ApplicationRecord

  def self.find_or_create_from_auth_hash(auth_hash)
    find_or_create_by(uid: uid) do |user|
            user.uid =auth_hash[:uid]
            user.twitter_id =auth_hash[:info][:nickname]
            user.twitter_name =auth_hash[:info][:name]
            user.twitter_icon =auth_hash[:info][:image]
    end
  end
end

ログイン、ログアウト用のリンクの追加

<% if logged_in? %>
  <%= link_to 'ログアウト', logout_path %>
<% else %>
  <a href="/auth/twitter">ログイン</a>
<% end %>

これでTwitterでログイン・ログアウトができるようになりました 🎉


何か間違えているところ等あればコメントやTwitterでメンションください。

*1:2021年12月時点でバージョン指定せずにomniauth-twitter をインストールするとomniauth のバージョンは2系になる