[Rails]TwitterAPI認証+deviseでのログインの実装方法

#経緯
railsにdeviseをインストールした状態でTwitterAPI認証でのログインを実装しました。
色んなサイトを参考にした結果、迷走し2日以上かけてしまいました。
今後も使うかもしれない自分用の備忘録的サムシングとして、またみなさんにササッと実装いただけたらと思い投稿しました。

#開発環境
ruby 2.7.0
rails 5.2.4
AWS EC2/RDS

#前提条件
・Twitterアカウントがある。
・deviseインストール済のアプリがある。
・環境変数の設定ができている。
・本番環境でアプリの動作ができている状態。

#TwitterのAPI認証取得および設定
a. API取得

https://qiita.com/kngsym2018/items/2524d21455aac111cdee
私はこちらのサイトを参考にしました。

callbackURLには以下のように入れてください。
URL : https://[自分のサイトのリンク]/users/auth/twitter/callback
(https化されてない場合は、うまく動作しないかもしれません。)

追記：
ローカルで動かす場合は、callback URLに
URL : http://localhost:3000/users/auth/twitter/callback
と入れていただくと動作するようです。


Terms of service URLとPrivacy policy URL何か埋めてください。次の項目で必須となってます。
こんな感じになります。
![スクリーンショット 2020-04-20 20.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574173/351d6cc3-e3ca-499a-bcab-6e3eb35e5ff9.png)

b. 取得できたAPI KeyとAPI Secretはメモ
![スクリーンショット 2020-04-20 20.36.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574173/b8f1abf2-a19d-53f4-6c46-f20325a4f438.png)

c. Permissionsの設定
read onlyなどは用途に合わせて選んでください。
ユーザー登録にメルアドが必要なので「Request email addresses from users」チェック
![スクリーンショット 2020-04-20 20.48.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574173/00f81209-14a8-673a-67d5-66c17de790c9.png)

#Gemの設定

```rb:Gemfile
gem 'omniauth'
gem 'omniauth-twitter'
```


忘れずに

```terminal
$ bundle
```


#API keyとAPI secretの設定
a. メモしたAPI keyとAPI secretを.envファイルに記述します。
ここではそれぞれの名前はTWITTER_CONSUMER_KEYとTWITTER_CONSUMER_SECRETにしています。
ドメインネーム（アプリのドメインorIPアドレス）も記述します

```text:.env
TWITTER_CONSUMER_KEY = "****************************"
TWITTER_CONSUMER_SECRET = "****************************"

DOMAIN_NAME = "*****************"
```
b. .gitignoreに記述

```text:.gitignore
.
.
.env
.
.
```

#deviseの設定
記述した認証用のキーをdeviseが識別できるように記述します。


```ruby:config/initializers/devise.rb
config.omniauth :twitter, ENV['TWITTER_CONSUMER_KEY'], ENV['TWITTER_CONSUMER_SECRET'], scope: 'email', oauth_callback: "#{ENV['DOMAIN_NAME']}/users/auth/twitter/callback"
OmniAuth.config.logger = Rails.logger if Rails.env.development? # debug用
```

#UserモデルとTwitter認証モデルを紐付ける
a. deviseに:omniauthable, omniauth_providers: [:facebook, :twitter, :google_oauth2]を追加
facebookとグーグル認証も将来的に使うかもしないので一応付けておきます。

```rb:app/models/user.rb
devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :validatable,
       # 外部API認証用に以下追加
       :omniauthable, omniauth_providers: [:facebook, :twitter, :google_oauth2]
```

b. マイグレーションファイルにカラムの追加

```terminal
$ rails g migration AddColumnsToUsers uid:string provider:string
$ rails db:migrate
```

c. Userモデルにfindメソッドを実装

```rb:app/models/user.rb
# Twitter認証ログイン用
# ユーザーの情報があれば探し、無ければ作成する
def self.find_for_oauth(auth)
  user = User.find_by(uid: auth.uid, provider: auth.provider)

  user ||= User.create!(
    uid: auth.uid,
    provider: auth.provider,
    name: auth[:info][:name],
    email: User.dummy_email(auth),
    password: Devise.friendly_token[0, 20]
  )

  user
end

def self.dummy_email(auth)
  "#{Time.now.strftime('%Y%m%d%H%M%S').to_i}-#{auth.uid}-#{auth.provider}@example.com"
end
```

メールアドレスでの認証も実装している場合は、OAuthでの認証時もメールアドレスを保存する必要があります。
emailはダミー用のアドレスを作成して代用します。
email中に時間まで記載しているのは、認証後に退会などで論理削除された後も再度認証ログインできるように今回は設定しています。


d. omniauthコントローラーの設定
deviseのusers配下にあります。
無い方は`$ rails g devise:controllers users`を実行すると見えるようになります。

```app/controllers/users/omniauth_callbacks_controller.rb
def twitter
  callback_from :twitter
end

private
  # コールバック時に行う処理
  def callback_from(provider)
    provider = provider.to_s

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

    # persisted?でDBに保存済みかどうか判断
    if @user.persisted?
      # サインアップ時に行いたい処理があればここに書きます。
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user, event: :authentication
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end
```

#ルーティングの設定
omniauth_callbacks_pathの追加

```ruby:config/routes.rb
devise_for :users, :controllers => {
    :registrations => 'users/registrations',
    :sessions => 'users/sessions',
    :passwords => 'users/passwords',
    # このpathを通して外部API認証が行われる。
    :omniauth_callbacks => 'users/omniauth_callbacks'
   }
```

#ビューに認証用リンクの設定
書き方はなんでもありだと思いますが一例として
a. loginページにリンク追加

```erb:app/views/devise/registrations/new.html.erb
<%= link_to user_twitter_omniauth_authorize_path do %>
  <i class="fab fa-twitter-square fa-2x"></i>
<% end %>
```

b. signupページにリンク追加

```erb:app/views/devise/sessions/new.html.erb
<%= link_to user_twitter_omniauth_authorize_path do %>
  <i class="fab fa-twitter-square fa-2x"></i>
<% end %>
```


c. cssにtwitterアイコンのカラーを追加

```css:app/assets/stylesheets/application.css.scss
.fa-twitter-square {
  color: #1da1f2;
}
```

#本番環境で動作確認
ここまでの操作でできているはずです。

a. アップロードの準備

.envのアップロード
ローカル環境でアプリケーション直下に移動して

```terminal
$ scp -i ~/.ssh/practice-aws.pem .env ec2-user@××.××.×××.×××:[アプリ名]/
```

本番環境に入って、

```terminal
// プリコンパイルを実行
$ bundle exec rails assets:precompile RAILS_ENV=production
// マイグレートを実行
$ bundle exec rails db:migrate RAILS_ENV=production
// サーバー起動
$ rails s -e production
```


b. ビューの確認 
![スクリーンショット 2020-04-21 14.16.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574173/4256faf9-48bf-f0a4-c471-72f7b5ee6389.png)
ツイッターのアイコンが表示出来ています。

c. 認証ログイン後確認
![スクリーンショット 2020-04-21 14.19.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574173/2b0069e0-6173-f16f-6194-e5f0ac16f998.png)
ツイッターでの表示名でログインできていることが確認できました。

#まとめ
TwitterAPIを使ってワンクリックでサインアップすることができるようになったため、よりユーザービリティが上がったと思います。
はじめてAPI認証でのログインに挑戦してみて、WEBのつながりが見えてきて非常に勉強になりました。

#今回時間がかかった要因
a. ローカルでの試験運用ができなかったこと
TwitterDeveloperのwebsiteURLに他サイトで推奨されているようなhttp://127.0.0.1:3000 などを入れても「invalid URL」といったメッセージが返ってくるため、(他のやりようがあったみたいですが少々面倒だったので)ローカル→本番→試験→NGを繰り返していました。

結果的にはめちゃめちゃ時間くっちゃいましたので、面倒でもローカルでの有効化方法を模索すればよかったと反省です。良い方法があれば教えていただけると嬉しいです。

b. deviseでの認証の場合、やり方が通常と異なること
通常では/config/initializers下にomniauth.rbというファイルを新規作成してコントローラーも別で作ってってやり方になると思いますが、そちらでやっていたためdeviseが入ってるとログイン認証はデバイスが絡んでいるため、エラーになっていました。
deviseの便利さに甘えて、大事なことに気づくことが遅れてしまいました。反省です。


##参考
・https://qiita.com/kazuooooo/items/47e7d426cbb33355590e
・https://qiita.com/zenizh/items/94aec2d94a2b4e9a1d0b
・https://qiita.com/puremoru0315/items/f1d459b663fd3b715dee
・https://kurose.me/twitter-omniauth/#Twitter