Help us understand the problem. What is going on with this article?

Railsでomniauth-twitterを使ってTwitter認証をする(2018/10)

More than 1 year has passed since last update.

概要

RailsのTwitterアカウントでの連携ログインについて、少しハマったのでメモしつつ、解決方法を提案できたらと思います。
TwitterAPIの仕様が数ヶ月前に変更されたあとの記事が少なく感じました(2018/10現在)。
また、deviseなるものとomniauth-twitterを連携させたものがあるようですが、今回はomniauth-twitter単体での実装です。また、作ったアプリケーションはHerokuにデプロイするところまで書きたいと思います。

開発環境

macOS High Sierra v10.13.6
ruby 2.5.1
rails 5.2.1
direnv
Heroku

準備

RailsやRubyの導入は省きます。

direnv

ディレクトリごとに環境変数を設定できるコマンドラインツールです。

導入

  • macOS
    Homebrewが入っているなら、コマンドひとつでインストールできます。
$ brew install direnv

インストール後は忘れずに「.bashrc」に設定を書きます。

$ echo 'export EDITOR=vim' >> ~/.bashrc
$ echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
$ source ~/.bashrc

詳しい使い方は次のURLを参照してください。
https://www.softantenna.com/wp/review/direnv/

Heroku

言わずとしれたデプロイサイト。railsアプリをpushするだけでデプロイが終わっちゃう驚き。
ローカル環境でOK!って人は飛ばしてください。
Herokuを導入する以前にgitを導入する必要があるんですが、ここでは省略します。
Herokuは次のURLからダウンロードしてください。
https://devcenter.heroku.com/articles/heroku-cli

TwitterAPI

個人的にちょっと厄介なこいつ。2018年9月の仕様変更によってAPIの使用に審査が必要になって、使用目的とか取得したデータの扱いとかを300文字以上の英語で送ります。Google翻訳を駆使したりしてがんばります。
APIの審査についてのURLです。ちなみに私は審査を出した当日に承認されました(2018/10)。
https://qiita.com/tdkn/items/521686c240b0c5bc6207

【重要】TwitterAPIの設定なんですが、CallBack_URLを適切に指定しないとエラーでます。
これ、意外とハマるんじゃないかと思います。
omniauth-twitterのみの場合、callback_urlは
http://localhost:3000/auth/twitter/callback
とかですね。
deviseをかませる場合少し違った気がします。
どこぞのQiita記事でlocalhostはcallback_urlに指定できないとか書いてたのでそれは間違いだって言いたいです。
TwitterAPIのCallBackについて次の記事を参考にしました。
https://saruwakakun.com/memo/omniauth-twitter

ローカル導入

まずは、ローカルでの構築を行っていきます。

新規アプリ作成とGem導入

$ rails new twitter-auth
$ cd twitter-auth/

twitter-authというアプリ名で作成します。次にGemfileを編集します。

gem 'omniauth'
gem 'omniauth-twitter'

Gemfileを編集したので忘れずにbundle installをします。

$ bundle install

APIキーの登録

用意したTwitterAPIを環境変数に設定します。

$ direnv edit .

vimが開きます。
vimの詳しい使い方についてはコチラ。
とりあえず、「i」キーを押してINSERTモードに変更して

export TWITTER_KEY="your_twitter_api_key"
export TWITTER_SECRET="your_twitter_api_secret"

書き終えたら「Esc」キーでINSERTモードを抜けて、
「:wq」と入力します。するとターミナルに戻ります。
環境変数はGitとかで公開してはダメなので、「.gitignore」に追記します。

$ echo ./.envrc >> .gitignore

次に、設定した環境変数をomniauthに設定します。ファイルが無いので新規作成して次を記述します。

config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
    provider :twitter,
    ENV["TWITTER_KEY"],
    ENV["TWITTER_SECRET"]
end

Userモデル作成

まずはモデルを作成します。

$ rails g model user provider:string uid:string user_name:string image_url:string

次にモデルの定義です。引数のauthはTwitterAPIのレスポンスデータで、様々な情報が格納されている。

app/model/user.rb
class User < ApplicationRecord
    def self.find_or_create_from_auth(auth)
        provider = auth[:provider]
        uid = auth[:uid]
        user_name = auth[:info][:name]
        image_url = auth[:info][:image]

        self.find_or_create_by(provider: provider, uid: uid) do |user|
            user.user_name = user_name
            user.image_url = image_url
        end
    end
end

この他にもデータを取得したい場合は次の一覧から適切な変数を指定することでデータを取得できる。
https://github.com/arunagw/omniauth-twitter#authentication-hash

ex) ログインした人のTwitterURLをtwitter_urlに格納する。

twitter_url = auth[:info][:urls][:Twitter]

Login/Registerコントローラ作成

ログイン、ログアウトに使うSessionコントローラを作成します。

$ rails g controller sessions

そして次を記述します。

app/controller/sessions_controller.rb
class SessionsController < ApplicationController
    def create
        user = User.find_or_create_from_auth(request.env['omniauth.auth'])
        session[:user_id] = user.uid
        flash[:notice] = "ユーザ認証が完了しました。"
        redirect_to root_path
    end

    def destroy
        reset_session
        flash[:notice] = "ログアウトしました。"
        redirect_to root_path
    end
end

ヘルパーメソッドの記述

current_user(ログイン中のユーザ情報)の取得やログイン中かどうかを返すヘルパーメソッドを定義します。

app/controller/application_controller.rb
class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception
    helper_method :current_user, :logged_in?, :authenticate

    private

    def current_user
      return unless session[:user_id]
      @current_user ||= User.find_by(uid: session[:user_id])
    end

    def logged_in?
      !!session[:user_id]
    end

    def authenticate
      return if logged_in?
      redirect_to root_path, alert: "ログインしてください"
    end
end

viewの設定

viewを定義します。今回は、pagesのindexをroot_pathとして、showをログインが必要なページとします。

rails g controller Pages index show

まずは、全体に適用するviewとして、

部分を下に変更してください。
app/view/layout/application.html.erb
<body>
  <header>
    <% if logged_in? %>
      <%= link_to 'Logout', '/signout' %>
    <% else %>
      <%= link_to 'Twitter Login', '/auth/twitter' %>
    <% end %>
  </header>
  <% if flash[:notice] %>
    <div class="notice">
      <%= flash[:notice]%>
    </div>
  <% end %>
  <%= yield %>
</body>

ぶっちゃけ、indexとshowは何も書かなくていいような気がするんですけど、一応わかりやすくするために書いてます。

app/view/pages/index.html.erb
<h1>TwitterAuthTest</h1>
<p>Find me in app/views/pages/index.html.erb</p>
<%= link_to 'User Page', '/pages/show' %>
app/view/pages/show.html.erb
<% if logged_in? %>
<h1>こんにちは、<%=  current_user.user_name %>さん</h1>
<p>ユーザー用ページです。</p>
<% else %>
<p>ログインしてください</p>
<% end %>

routesの設定

routesの設定です。

config/routes.rb
Rails.application.routes.draw do 
  root 'pages#index'
  get 'pages/show'
  get '/auth/:provider/callback', to: 'sessions#create'
  get '/signout', to: 'sessions#destroy'
end

ローカルで動かす

以上で導入は完了です。ローカルで動かすためにはマイグレーションをするのを忘れないでください。

$ rails db:migrate
$ rails s

起動画面

起動画面はこんな感じです。
スクリーンショット 2018-10-20 4.15.07.png
Twitterでログインをしたら。
スクリーンショット 2018-10-20 4.15.55.png
User Pageをクリック。
スクリーンショット 2018-10-20 4.15.37.png
(一部隠してます)

Herokuにデプロイ

Herokuにデプロイしてみます。

データベースシステムの変更

まず、現在使用しているSQLite3はHerokuで使えない仕様なので、PostgreSQLを使うようにGemを変更します。

  • 上の方にある「gem 'sqlite3'」は削除するか、先頭に#をつけてコメントアウトしましょう。
  • 「group :development do」の中に「gem 'sqlite3'」を書きます。
  • 「group :production do」を追記して中に「gem 'pg'」を書きます。(endを忘れずに書きます。)

これは、データベースシステムを開発環境ではSQLite3を使って本番環境 (Heroku)ではPostgreSQLを使いますという意味です。

.
.
# gem 'sqlite3'
group :development do
  .
  .
  gem 'sqlite3'
end

group :production do
  gem 'pg'
end
.
.

今後、ローカルで開発するときのためにオプションをつけてbundle installします。

$ bundle install --without production

Rails Tutorialでもおなじみのこれです。groupがproductionのものはローカルにインストールしないという意味です。

いざデプロイ

herokuにデプロイする準備ができたのでデプロイします。

$ heroku create [your_heroku_app_name]
$ git add .
$ git commit -am "init"
$ git push heroku master
$ heroku run rails db:migrate
$ heroku config:set TWITTER_KEY=your_twitter_api_key
$ heroku config:set TWITTER_SECRET=your_twitter_api_secret

TwitterAPIのページでCallBack_URLを設定します。これがないとエラー吐きます。
CallBackに以下のリンクを追加します。
https://[your_heroku_app_name].herokuapp.com/auth/twitter/callback

うまくデプロイできれば次のURLでアプリケーションが動いてるところを見ることができます。
https://[your_heroku_app_name].herokuapp.com/

ハマったポイント

ここから恥ずかしい話になります。
これを実装するのに3日ほどかかりました。なぜかというと、400エラーやら403エラーに襲われたからです・・・。
もしかしたら同じようなエラーで困っている人のために解決法を書いておきます。

400エラー

私の場合、TwitterのAPIキーがうまく設定できておらず、APIリクエストを投げる際にAPIキーを送れていなかったときに出ました。
direnvの設定で.bashrceval "$(direnv hook bash)"を書き忘れていたおかげでrailsが.envrcを読まないという原因でした。

403エラー

TwitterAPi設定で、CallBackURLを正しく指定していなかったときに発生しました。
ローカルで開発の場合は、
http://localhost:3000/auth/twitter/callback
http://127.0.0.1:3000/auth/twitter/callback
この2つを設定しておきましょう。
Herokuなどにデプロイした場合も、その都度callback_urlを設定するのを忘れないようにしましょう。

まとめ

Railsにomniauth-twitterを用いたTwitter連携ログイン機能について軽く説明を交えつつ実装方法を書きました。
Rails初心者が書いた&コピペなので間違ったコードや間違った説明などしている所あるかもしれません。
今回の開発をgithubに上げました。
https://github.com/white0221/twitter_auth_on_rails
最後まで読んでいただきありがとうございました。
開発の助けになれば幸いです。

参考

https://github.com/arunagw/omniauth-twitter
https://qiita.com/puremoru0315/items/f1d459b663fd3b715dee
https://qiita.com/ntkgcj/items/c3108c19fb64acc9dd8d

white0221
プログラミングで自分がはまったところについてまとめたいなって思ってます。
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