Ruby on RailsでTwitter認証だけのログイン機能を実装する
既にいろんなTwitter認証を実装する記事がありますが、2021年に行われたomniouth2系の仕様変更で詰まったので自分なりにまとめておきます。
環境
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
TwitterAPIの取得
Twitterと連携するためには、TwitterのAPIキーとAPIシークレットキーが必要です。
申請方法などはこの記事では省略します。比較的最新の記事を参照することをおすすめします。
2021/9に申請を出したところ、すぐに承認メールがきました。用途や申請内容によって待ち時間が変わってくるようです。
TwitterのAPIキーとAPIシークレットキーを設定
APIの認証情報を登録する前に、GitHunにアクセスキーを公開させないために 'dotenv-rails'
をインストールします。
gem 'dotenv-rails'
本番環境でherokuなどを使う場合は、Gemfileはgroup :development, :test
の中に入れます。
別途heroku側で環境変数を設定する必要があります。
.env
ファイルを作成し、取得した各種キーを設定します。
TWITTER_API_KEY = 'hogehgoe' TWITTER_API_SECRET = 'fugafuga'
omniauth-twitter導入
omniauth-twitter
を導入します。
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系の場合*1、rails server
でサーバーを起動して直接URLを叩いてみるとエラーになり、No route matches [GET] "/auth/twitter"
というエラー画面に遷移するはずです。
というのも、OmniAuth1系までは認可画面へリダイレクトする際、デフォルトでGET/POSTどちらも有効でしたが、CSRF脆弱性の対応に伴い2.0からはPOSTのみに変更になったためです。
参考:
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 %>
を記載し、画面上から確認します。
画面からログインボタンをクリックすると見たことがあるであろう画面が出てくるはずです。 (ここでまたエラー画面が出る場合は認証キーの設定が間違えているなどの他の原因が考えられます)
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を参照してください。
今回は試しに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でメンションください。