生涯学習エンジニヤ(忍)

エンジニア忍者としての修行の記録です。

Rails3 に Twitter認証を最速で入れる

Rails3でTwitter連携

f:id:nekotool:20160715182041p:plain

Rails3で、Twitterログインしたり、TwitterでツイートしたりできるようにするためのOAuth認証を入れる方法を記録します。なお、 Facebook 等、他のOAuth対応プロバイダにも拡張可能な形で実装します。

〜目次〜

Twitter公式から API Key を取得

https://apps.twitter.com にログインして新規アプリを作成する。
Consumer Key と Consumer Secret を取得する。
f:id:nekotool:20160715170458p:plain

Gemfile に追加

以下2つの gem を追加する。

gem 'twitter'
gem 'omniauth-twitter'

ターミナルで bundle install する。

bundle install

initializer で OmniAuth の初期化

config/initializers/omniauth.rb を作成し、 Twitter公式から取得した API Key を入力する。

Rails.application.config.middleware.use OmniAuth::Builder do
  # provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
  provider :twitter, 'AAAAAAAAAAAAAAAAAAAAAAA', 'BBBBBBBBBBBBBBBBBBBBBBBBBB'
end

ユーザの認証情報を格納するモデル AuthUser の作成

rails g model auth_user provider:string uid:string name:string credentials:string user_info_raw:text

作成された migration ファイルを編集.

class CreateAuthUsers < ActiveRecord::Migration
  def change
    create_table :auth_users do |t|
      t.string :provider, :null => false, :limit => 16
      t.string :uid, :null => false
      t.string :name
      t.string :credentials
      t.text   :user_info_raw

      t.timestamps
    end

    add_index :auth_users, [:provider, :uid], :unique => true
  end
end
  • provider は 'twitter' や 'facebook' などプロバイダ識別子が入る。
  • uid は、プロバイダの提供する、認証ユーザの識別子(Twitter だと、数字)
  • name は、プロバイダの提供する、認証ユーザの名前 (Twitter だと、ユーザID @xxxx の ’xxxx' の部分)
  • credentials は、認証ユーザのアクセストークンとシークレットを、区切り文字でつなげて格納する。
  • user_info_raw は、認証ユーザの、細かいユーザ情報を Hash 形式の文字列で格納する。

※別のユーザ関連モデルと関連づける場合は、 user_id: integer 等のカラムを追加する必要があるが、今回はしない。

マイグレーションをする。

rake db:migrate

AuthUser モデルの編集

app/model/auth_user.rb を編集する。

# -*- coding: utf-8 -*-
class AuthUser < ActiveRecord::Base
  attr_accessible :name, :provider, :uid, :credentials, :user_info_raw

  # :credentials カラムの token と key を分わけるための分割記号
  CRED_SPLIT = '|%|'

  ##################################
  # Accessrors
  ##################################
  def user_info
    @user_info ||= eval(user_info_raw)
  end

  def token
    credentials.split(CRED_SPLIT)[0]
  end
  def secret
    credentials.split(CRED_SPLIT)[1]
  end
  def screen_name
    prefix = (provider == "twitter") ? "@" : ""
    prefix + user_info["name"]
  end
  def nickname
    user_info["nickname"]
  end
  def img_url
    user_info["image"]
  end

  ##################################
  # Find/Create/Update
  ##################################

  def self.find_or_create_with_omniauth auth
    au = AuthUser.find_by_provider_and_uid auth["provider"], auth["uid"]
    if au
      au.update_with_omniauth auth
    else
      au = AuthUser.create_with_omniauth auth
    end
    au
  end

  def self.create_with_omniauth auth
    create! do |a_user|
      a_user.provider = auth["provider"]
      a_user.uid      = auth["uid"]
      a_user.update_with_omniauth auth
    end
  end

  def update_with_omniauth auth
    a_user = self
    a_user.name           = auth["info"]["name"]
    a_user.credentials    =
      auth['credentials']['token'] + CRED_SPLIT + auth['credentials']['secret']
    a_user.user_info_raw  = auth["info"].to_hash.inspect
    a_user.save!
    self
  end

end

Twitter 認証時、 Auth.find_or_create_with_omniauth を呼んで、AuthUser 情報を作成・更新する。
「Accessrors」 の部分に記載したものは、以下を意味する。

  • user_info は、ユーザの細かい情報
  • token は、アクセストークン
  • secret は、アクセストークのシークレット
  • screen_name は、@つきのTwitter ID '@xxxx' を返す
  • nickname は、ユーザが設定したニックネーム文字列( "エックスフォーさん" など)
  • img_url は、アイコン画像のURL

Twitter コールバック用のURLを設定

Twitter 公式の認証ページに飛び、アプリが承認された後に戻ってくるページを、Twitterに設定する。
パスを /auth/twitter/callback とする。
https://apps.twitter.com に、以下のURLを設定する。

http://0.0.0.0:3000/auth/twitter/callback

f:id:nekotool:20160715174235p:plain

ルーティング設定を追加する

config/routes.rb に以下を追加する

+  match "/auth/:provider/"         => "auth_sessions#auth"    # /auth/twitter
+  match "/auth/:provider/callback" => "auth_sessions#create"  # /auth/twitter/callback
+  match "/auth/signout"            => "auth_sessions#signout" # /auth/signout
  • /auth/twitter => Twitter 認証する時に開くURLで、Twitter公式の認証ページへのリダイレクトを行う。
  • /auth/twitter/callback => Twitter公式の認証ページで認証された後に戻ってくるページ。Twitter から情報を受け取ることになるので、ここで AuthUser モデルを生成/更新する。
  • /auth/twitter/signout => 認証状態の解除用のパスで、 session から AuthUser#id を削除する。

上記に出てくる auth_sessions というコントローラは、これから作成する。

認証状態を記録する AuthSession コントローラの作成

以下を実行する。

rails g controller auth_sessions

作成された AuthSessions コントローラ app/controllers/auth_sessions_controller.rb を編集する。

class AuthSessionsController < ApplicationController

  # redirect to provider's authorizing page.
  def auth
    consumer_key = 'AAAAAAAAAAAAAAAAAAAAAAA'
    url = case params[:provider]
          when "twitter"
            "https://api.twitter.com/oauth/authorize?oauth_token=" + consumer_key
          else
            raise "Unknown provider error."
          end
    redirect_to url # Twitter 公式の認証ページへ飛ぶ
  end

  # /auth/:provider/callback
  def create
    auth = request.env["omniauth.auth"]
    auth_user = AuthUser.find_or_create_with_omniauth auth
    session[:auth_user_id] = auth_user.id
    redirect_to '/' # とりあえずトップページに移動
  end

  # /auth/signout
  def signout
    session[:auth_user_id] = nil
    render :text => '認証を外しました。' # とりあえず
  end

end

※ def auth に consumer key をベタ書きしているが、実際はグローバル参照可能なクラスに定数として定義しておく。

認証成功を見るための view を一時的に index に作成する。

index.html.erb を編集

<% au = AuthUser.find_by_id(session[:auth_user_id) %>
<% if au %>

<pre>
id: <%= au.id %>
provider: <%= au.provider %>
uid: <%= au.uid %>
name: <%= au.name %>
screen_name: <%= au.screen_name %>
nickname: <%= au.nickname %>
img_url:  <%= au.img_url %>
<img src="<%= au.img_url %>" />
credentials: <%= au.credentials %>
token: <%= au.token %>
secret: <%= au.secret %>
user_info: <%= au.user_info %>
</pre>

<% else %>

認証ログインしていません。

<% end %>

そうすると、 /auth/twitter にアクセスすると、Twitter認証ページに飛び、認証すると、 /auth/twitter/callback に情報とともに戻ってくる。そこで AuthUser インスタンスを作成&保存し、 トップにリダイレクトする。
以下のように表示される。
f:id:nekotool:20160715181033p:plain

この AuthUser モデルのインスタンスを利用すると、色々なことができる。
終わり。

認証状態を解除するときは

/auth/twitter/signout にアクセスする。

この先にできること

au.token, au.secret を利用すれば、認証ユーザのアカウントでツイートできる。
au.img_url を、ユーザのアカウント画像にするのもありだろう。

他にやること

AuthUser モデルを、別のモデル、例えば既存の User モデルに紐づける場合は、 AuthUser に user_id :integer カラムを追加して、関連する User#id と紐づけるようにする。