Rails5 Twitterログインをdevise+omniauthで実装

Twitterログインをdeviseとomniauthで実装する方法を説明します。

## 環境
Rails 5.2.1

## 手順
1. Twitter Developer登録
2. credentials.yml.encにConsumer API keysを設定
3. gem追加
4. deviseの設定
5. userモデルの設定
6. callbackの設定
7. viewの設定
8. route設定

## Twitter Developer登録
[Twitter Developer](https://developer.twitter.com/) でアプリを登録
ローカルで試すときは callback urlに http://127.0.0.1:3000/users/auth/twitter/callback を指定
Consumer API keysを取得

## credentials.yml.encにConsumer API keysを設定
Rails5.2では新規アプリを作成した時にconfig/secrets.ymlが生成されず、代わりにconfig/credentials.yml.encが生成されるようになりました。

このcredentials.yml.encに先ほど取得したConsumer API keysを設定

```console
$ EDITOR="vim" bin/rails credentials:edit
```

```vim:credentials.yml.enc
twitter:
  twitter_api_key: API keyを入力
  twitter_api_secret: API secret keyを入力

# aws:
#   access_key_id: 123
#   secret_access_key: 345
```


## gem追加
Gemfileにdeviseとomniauth-twitterのgemを追加

```swift:Gemfile
gem 'devise'
gem 'omniauth-twitter'
```

bundle installを実行

```console
$ bundle install
```

## deviseの設定
下記のコマンドを実行

```console
$ rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================
```

ターミナルで言われた通り設定を行う

1.　デフォルトURLの指定

```ruby:config/environments/development.rb
# mailer setting
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```
2.　root_urlの指定
routes.rbでrootを指定


次に、devise.rb(260行目くらい)にConsumer API keysを設定

```ruby:config/initializers/devise.rb
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :twitter, Rails.application.credentials.twitter[:twitter_api_key], Rails.application.credentials.twitter[:twitter_api_secret], display: 'popup'
```

## userモデルの設定
userモデルを作成

```console
$ rails g devise User
```

以下を実行してomniauth-twitter用のカラムを追加

```console
$ rails g migration add_columns_to_users provider uid name nickname image
$ rake db:migrate
```

user.rbを下記のように編集

```ruby:models/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(
       uid:      auth.uid,
       provider: auth.provider,
       email:    User.dummy_email(auth),
       password: Devise.friendly_token[0, 20],
       image: auth.info.image,
       name: auth.info.name,
       nickname: auth.info.nickname,
       )
   end

   user
  end

  private

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

## callbackの設定
controllers/usersにomniauth_callbacks_controller.rbを作成して以下を記述

```ruby:controllers/users/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?
      print("persisted true")
      flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize)
      sign_in_and_redirect @user, event: :authentication
    else
      print("persisted false")
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to controller: 'sessions', action: 'new'
    end
  end
end
```

## viewの設定
任意のviewで

```erb
<%= link_to 'Twitterログイン', user_twitter_omniauth_authorize_path %>
```

## route設定
config/routes.rbを以下を追加

```ruby:config/routes.rb
devise_for :users, controllers: {
    omniauth_callbacks: 'users/omniauth_callbacks'
  }
```

## 確認
サーバーを起動して確認

```console
$ rails s
```

後は、devise導入によって使えるようになったヘルパーメソッドを使って自由に実装


## 参考
[Rails5.2から追加された credentials.yml.enc のキホン](https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39)
[[*Rails*] deviseの使い方（rails5版）](https://qiita.com/cigalecigales/items/f4274088f20832252374)
[Rails deviseで使えるようになるヘルパーメソッド一覧](https://qiita.com/tobita0000/items/866de191635e6d74e392)