twitterアカウントでログイン devise+omniauth (rails5)

#概要
Rails5系で`devise`と`omniauth`を利用してtwitterログインの実装をテスト のメモ。

rubyのバージョン管理は[rbenv](https://qiita.com/shinkuFencer/items/3679cfd966f6a61ccd1b)で
環境変数の管理には[direnv](https://deeeet.com/writing/2014/05/06/direnv/) 使ってます。
ファイル編集はVim

#環境
macOSX
rails 5.1.5
ruby 2.5.0
rbenv
bundler
direnv
vim

#参考
>[[*Rails*] deviseの使い方（rails5版）](https://qiita.com/cigalecigales/items/f4274088f20832252374)
[https://github.com/tmk815/Twitter_OmniAuth <- ほぼこちらの模倣です](https://github.com/tmk815/Twitter_OmniAuth)

#うまくいかないときに確認すべきファイル等

```ruby
#（プロバイダ毎の）コールバック処理など
vim app/controllers/users/omniauth_callbacks_controller.rb
#deviseのモジュール設定や、ユーザー登録処理など（userのDBにカラム設定していないものは情報保存できないので注意）
vim app/models/user.rb
#ルーティング設定（OAuthコールバック処理）
vim config/routes.rb
#deviseでのAPIキー設定など（development.rbに記述する場合は不要かも?）
vim config/initializers/devise.rb
#ログイン済みか確認する処理
vim app/controllers/application_controller.rb 
```

#準備
今回は例で
ワークスペース位置を`~/Projects`
プロジェクト名を`devise_omniauth_sample`
としています。

```zsh
cd ~/Projects
bundle init
echo -e "ruby '2.5.0'\ngem 'rails'" >> Gemfile
rbenv local 2.5.0
bundle install --path vendor/bundle
bundle exec rails new devise_omniauth_sample --skip-bundle

rm -f Gemfile Gemfile.lock | rm -rf .bundle vendor/bundle

#必要に応じて通常使用rubyの戻し指定
#rbenv local 2.0.0-p648
```

```zsh
cd ~/Projects/devise_omniauth_sample
rbenv local 2.5.0
echo -e "gem 'devise'\ngem 'omniauth-twitter'" >> Gemfile
bundle install --path vendor/bundle
echo '/vendor/bundle' >> .gitignore

bundle exec rails g devise:install
bundle exec rails g controller Pages index show
bundle exec rails g devise:views
bundle exec rails g devise User
bundle exec rails g migration add_columns_to_users provider uid username

bundle exec rake db:migrate
```
#構築
下記ファイルを編集していきます。

```rb
vim config/routes.rb
vim app/views/layouts/application.html.erb
vim app/models/user.rb
vim app/views/pages/index.html.erb
vim app/controllers/application_controller.rb
```

---
##各項目の記述内容

###ルーティング設定
ルートアクセス時の設定記述
`devise_for`の部分にコールバック時の設定を記述

```rb:config/routes.rb
  root 'pages#index'
  get 'pages/show'

  devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
```
###viewのレイアウト表示
ログイン・ログアウト等を表示させる

`<body>`直下に記述 ↓

```erb:app/views/layouts/application.html.erb
     <header>
         <nav>
             <% if user_signed_in? %>
             <strong><%= link_to current_user.username, pages_show_path %></strong>
             <%= link_to 'プロフィール変更', edit_user_registration_path %>
             <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
         <% else %>
             <%= link_to 'サインアップ', new_user_registration_path %>
             <%= link_to 'ログイン', new_user_session_path %>
             <% end %>
         </nav>
     </header>

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

###ユーザー登録処理
`devise`のモジュールに`:omniauthable`を追加（手前に , を忘れずに）
残りは`end`手前に記述 ↓

```rb:app/models/user.rb
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :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),
        username: auth.info.nickname,
        password: Devise.friendly_token[0, 20]
      )
    end
      
    user
  end
      
  private
      
  def self.dummy_email(auth)
    "#{auth.uid}-#{auth.provider}@example.com"
  end
```

###表示されるviewファイル
内容は~~パク~~適当です。（適宜自由に編集）

```erb:app/views/pages/index.html.erb
<% if user_signed_in? %>
<h1>こんにちは、<%= current_user.username %>さん</h1>
<p>ユーザー用ページです。</p>
<% else %>
<p>ログインしてください</p>
<% end %>
```
###ログインしているかチェックする設定

```rb:app/controllers/application_controller.rb
protect_from_forgery with: :exception
before_action :authenticate_user!
```
---
#テスト用SNSアプリ設定（API等）

- [Twitter の Application Management](https://apps.twitter.com/)登録

上記は用意済みの前提で以下の作業を進める

#環境変数の設定
[direnvのインストール](https://qiita.com/ntkgcj/items/125892b4459bc469f7ac)
###direnv設定

```zsh
direnv edit .
```
上記で取得済みのAPIキー2種を記述する。

```ruby:.envrc
export TWITTER_CONSUMER_KEY="twitterのAPIキー"
export TWITTER_CONSUMER_SECRET="twitterのAPI_Secretキー"
```
git管理から除外

```zsh
echo '/.envrc' >> .gitignore
```

#SNS認証処理用の設定

###APIキー読込み設定


```ruby:config/initializers/devise.rb
# ==> OmniAuth
#config.omniauth :twitter, ENV['TWITTER_CONSUMER_KEY'], ENV['TWITTER_CONSUMER_SECRET']
  twitter_consumer_key = ENV['TWITTER_CONSUMER_KEY']
  twitter_consumer_secret = ENV['TWITTER_CONSUMER_SECRET']
  config.omniauth :twitter, twitter_consumer_key, twitter_consumer_secret
```

###コールバック用コントローラーの作成
```zsh
bundle exec rails g controller users/omniauth_callbacks
vim app/controllers/users/omniauth_callbacks_controller.rb
```

```rb:app/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?
      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
end
```
#確認
サーバー起動して

```zsh
bundle exec rails s
```
http://localhost:3000/
にアクセスして「Twitterアカウントでログイン」できるか確認してみる。