【Rails】Twitter認証機能実装の備忘録

#はじめに
オリジナルアプリにTwitter認証機能を実装する際に、いくつかハマったポイントがあったため、忘れないためにも一連の実装の流れをまとめてみたいと思います。

#環境
- Ruby 2.5.3
- Ruby on Rails 5.2.3
- PostgreSQL 11.4

#前提
- Deviseがインストール済みであること
- Userモデルが作成済みであること

#TwitterAPIの取得
この項目に関しては[こちら](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)の記事が非常に参考になりましたので、ぜひそちらを見ていただくといいかと思います。

#gemのインストール
今回必要となるgemは以下になります。

``` Gemfile
gem 'omniauth'
gem 'omniauth-twitter'
```

インストールしていきます。

```
$bundle install
```


#Userモデルに必要なカラムの追加
今回追加するカラムは以下になります。

- uid(string)　　　　・・・ ユーザー毎に異なる
- provider(string)　　・・・ ツイッターログイン情報の保存
- name　　　　　　・・・ ユーザーネーム

私の場合のTwitter認証で使用したカラムは最終的に以下になりました。
この辺はアプリによって自由に変更してください。

- uid(string)
- provider(string)
- name(string)
- email(string)
- avatar(string)
- password(string)

では追加していきます。

```
$rails g migration AddColumnsToUsers provider:string uid:string name:string
```

``` xxxxxx_add_columns_to_users.rb
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :name, :string
  end
end
```

忘れずにDBに反映しておきましょう。

```
$rails db:migrate
```
これでモデルの準備が整いました。

#APIkeyの設定

```config/initializers/devise.rb
Devise.setup do |config|
#省略

config.omniauth :twitter, ENV['API_KEY'], ENV['API_SECRET'],
                          callback_url:"各々のAPI取得時に設定したcallback_url"


```

開発環境と本番環境を分けて記述する場合には、 `if Rails.env.development?`等のif文で分けて書くことで区別できます。また、APIkeyは` Githubに上げないように `注意してください。

#Userモデルの編集
ここで行うことは以下になります。

- Deviseのモジュールの追加
    - Deviseの`omniauthable`を追加します。
- findメソッドの追加
    - ログインしているユーザー情報を取得します。
- ダミーのメールアドレスの準備
    - Deviseでログインするにはメールアドレスが必要なので、メールアドレスを保存する必要があります。

編集後のuser.rbは以下の通りです。

```user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable

#省略
  

 def self.find_for_oauth(auth)
    user = User.where(uid: auth.uid, provider: auth.provider).first

    unless user
      user = User.create(
        provider: auth.provider,
        uid:      auth.uid,
        name:     auth.info.nickname,
        email:    User.dummy_email(auth),
        remote_avatar_url:   auth.info.image,
        password: Devise.friendly_token[0, 20]
      )
    end
    user
  end

#省略

  
  private


  def self.dummy_email(auth)
    "#{auth.uid}-#{auth.provider}@example.com"
  end

#省略

```

#コールバックの設定
まず、app/controllers/usersの階層に`omniauth_callbacks_controller.rb`があることを確認してください。おそらく`rails g devise:controllers users`をした段階で作成されるはずです。無い場合は作成してください。

```
$rails g controller omniauth_callbacks
```

omniauth_callbacks_controller.rbに以下のように編集します。

```omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def twitter
    callback_from :twitter
  end

  private

  def callback_from(provider)
    provider = provider.to_s

    @user = User.find_for_oauth(request.env['omniauth.auth'])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication
    else
      redirect_to new_user_registration_url
    end
  end
end
```

>omniauth.authという変数に入っている情報を元にユーザーが登録されているか判別して、登録されていたらログインする、登録されていない場合は登録ページに遷移するようにします。
> [引用元](https://techblog.gmo-ap.jp/2019/02/08/rails%E3%81%A7omniauth-twitter%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6twitter%E8%AA%8D%E8%A8%BC%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B/)

ユーザー認証の成功・失敗した際のフラッシュメッセージ等を追加しておくと良いと思います。

#ルーティングの設定

```routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'users/registrations',
    sessions: 'users/sessions',
    passwords: 'users/passwords',
    omniauth_callbacks: 'users/omniauth_callbacks' #ここに追加
  }
```

これで実装部分は完了です！　あとはViewで確認してみましょう。

```app/views/devise/registrations/new.html
 = link_to user_twitter_omniauth_authorize_path, class: 'twitter-link' do
   ツイッターでログイン
```

<img width="801" alt="スクリーンショット 2019-12-07 21.41.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451305/d11aa8da-a7b7-8bd1-7199-3210b7cd3b6a.png">

ちゃんとログインできていれば完成です。

#躓いたポイント
- ツイッターのDeveloperアカウントでは認証できない。  
最初、私はDeveloperアカウントで何度も試して躓いていました。
試して見る際はAPIを取得したアカウントとは別のアカウントでログインできるか試してみてください。
公式ドキュメントをちゃんと読んだ方がいいですね。

- 401(Unauthorized)エラー  
私の場合はAPIkeyを誤って入力していただけだったのですが、他の記事などを見ても基本的な設定部分で間違っているケースが多い気がします。API取得時の設定やDeviseでの設定をもう一度見直してみてください。
お恥ずかしいですが、`Access token & access token secret`を入力していました...


今回はこれで以上になります。

#参考

- [Railsでomniauth-twitterを利用してTwitter認証を実装する](https://techblog.gmo-ap.jp/2019/02/08/rails%E3%81%A7omniauth-twitter%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6twitter%E8%AA%8D%E8%A8%BC%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B/)
- [Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ　※2019年8月時点の情報](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)