Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
44
Help us understand the problem. What is going on with this article?
@d0ne1s

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

簡単な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を導入します。

Gemfile
gem 'dotenv-rails'
$ bundle install

プロジェクトルートに.envファイルを作成し、上記で取得した各種キーを書き込みます。

TWITTER_API_KEY = hogehoge
TWITTER_API_SECRET = foobarbaz

omniauth-twitter

Twitter連携のためのgemomniauth-twitterを導入します。

Gemfile
gem 'omniauth-twitter'
$ bundle install

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のID、ニックネーム、ユーザー名、画像パスを保存するためのデータベースと関連するモデルを作成します。
上記は全てomniauth-twitterで取得できる値です。その他の値についてはomniauth-twitterのREADMEをご覧ください。

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

セッション管理用のヘルパーメソッドを作成

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

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

$ rails g controller Sessions
app/controllers/sessions_controllers.rb
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

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

app/views/layouts/_header.html.erb
<header class="header">
.
.
  <% if logged_in? %>
    <%= link_to 'ログアウト', logout_path %>
  <% else %>
    <a href="/auth/twitter">ログイン</a>
  <% end %>
.
.
</header>

ルーティングを設定

config/routes.rb
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をインストールして使えるようにします。

Gemfile
group :test do
# 省略
  gem 'rack_session_access'
end
$ bundle install
config/environments/test.rb
Rails.application.configure do
# 省略
  config.middleware.use RackSessionAccess::Middleware
spec/spec_helper.rb
RSpec.configure do |config|
# 省略
  require 'rack_session_access/capybara'
end
spec/systems/sample_spec.rb
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

参考

44
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
d0ne1s
RailsとTailwindが好きなフリーランスエンジニア。 Qiitaの記事検索サービスqiigle.comを運営。 最近はAWSを勉強しなおしてます。 Twitterものぞいてくれるとうれしいです🦈
admin-guild
「Webサービスの運営に必要なあらゆる知見」を共有できる場として作られた、運営者のためのコミュニティです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
44
Help us understand the problem. What is going on with this article?