Devise + Bootstrap3 + RailsLayoutにdevise-two-factorを使ってGoogle Authentificatorを追加する

#はじめに
以前の記事でDevise + Bootstrap3 + RailsLayout環境を作った。これにdevise-two-factorを使ってGoogle Authentificatorを追加してみる。参考記事はRails4なのでそのままでは動かない事に注意。

前提環境
Rails 5.2.2
Ruby 2.5.3
Devise-two-factor 3.0

#セットアップ手順
##1. 前の記事の手順でベースとなる環境を作る。
前の記事　DeviseのフォームをBootstrap3 + RailsLayoutでカッコ良くする
https://qiita.com/NYC-Blue/items/52fee61958a3a54d44d8

##2. `devise-two-factor` gemをインストールする。
Gemをインストールします。QRコードも生成するのでrqrcodeも入れておきます。

```ruby:/Gemfile
# Devise-two-factor 3.0
gem 'devise-two-factor', '~>3.0.0'
gem 'rqrcode-rails3'
```
`$ bundle install`

##3. 環境変数を定義する。
環境変数名は何でも良いのですが、私は`credentials.yml.enc`にGOOGLE_AUTH_ENCRYPTION_KEYを作りました。32文字くらいにすれば良いと思います。

`$ rails credentials:edit`

```ruby:結果
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: hoge...hoge
GOOGLE_AUTH_ENCRYPTION_KEY: foo...bar
```

##4. セットアップを実行する。
`$ rails generate devise_two_factor users GOOGLE_AUTH_ENCRYPTION_KEY`

READMEの説明
MODEL: 認証対象のモデル(今回は`Users`)
ENVIRONMENT_VARIABLE: 暗号化キーを入れる環境変数名(GOOGLE_AUTH_ENCRYPTION_KEY)
実行すると自動で次の処理が行われる。

`app/models/users.rb`のdeviseディレクティブ調整
`:database_authenticatable`を削除しようとするが、書き方によっては削除されない場合があるので、実行後要確認。削除されていなかったら手動で削除。
deviseの設定ファイルを調整
wardenの設定

Usersモデルに2段階認証関連のカラムを追加するためのマイグレーションファイルを生成するので`rake db:migrate`を実行します。

```ruby:db/migrate/20181231180452_add_devise_two_factor_to_users.rb
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :encrypted_otp_secret, :string
    add_column :users, :encrypted_otp_secret_iv, :string
    add_column :users, :encrypted_otp_secret_salt, :string
    add_column :users, :consumed_timestep, :integer
    add_column :users, :otp_required_for_login, :boolean
  end
end
```

`$ rake db:migrate`

##5. Devise Userモデルを修正
私の環境ではDeviseを既にカスタマイズしてしまっているからだと思いますが、READMEには自動セットアップと書いてありますが、user.rbの自動修正はされませんでしたので手で修正します。

```ruby:app/models/user.rb修正後
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :registerable,
         :recoverable, :rememberable, :validatable,
         :two_factor_authenticatable,
         :otp_secret_encryption_key => Rails.application.credentials.GOOGLE_AUTH_ENCRYPTION_KEY
end
```

##6. recoverableを使う場合はパスワードリセット後の自動ログインをさせない。

```ruby:config/initializers/devise.rb
  # When set to false, does not sign a user in automatically after their password is
  # reset. Defaults to true, so a user is signed in automatically after a reset.
  config.sign_in_after_reset_password = false   <=220行目あたり
```

##7. applicationコントローラーをカスタマイズ
READMEの通りに行いました。Devise 4.0.0以上の場合は以下の通り。私は個別にコントローラーを書き換えるのが面倒なのでapplication_controller.rbに書きましたが、細かくやりたい場合はDeviseのコントローラーを生成（`$ rails gdevise:controllers Users`）して、1つ1つ書いても良いでしょう。その場合はapp/controllers/users/にたくさんコントローラーのテンプレートができますので、必要なところを書き換えます。また、私のroute.rbはapp/controllers直下にDeviseのコントローラーを置く前提なので、rails gで作った場合は　{users/sessions} のようにしてください。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception

  before_action :configure_permitted_parameters, if: :devise_controller?

    protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt])
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email_confirmation])
    devise_parameter_sanitizer.permit(:account_update, keys: [:email_confirmation])
  end
end
```

ここまでで基本的なセットアップは完了です。

#カスタマイズ
ここからはやりたい動きに合わせてカスタマイズします。私がやったのは次の通り。

①サインアップしたらGoogle Authenticatorに読み込む為のQRコードを表示 => `registrations_controller.rb`
②サインインしてIDとパスワードが正しければGoogle Authenticatorのコードを入れる画面を表示 => `sessions_controller.rb`
③コードが間違っていたらコード入力画面に戻る => `sessions_controller.rb`

##1. Sign-up後にGoogle AuthentificatorのQRコードを出す。
Sign-upでメールアドレスが得られるのでDeviseのregistrationコントローラーをカスタマイズしてQRコード生成画面に飛ばす。

```ruby:app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
    protected

    def after_sign_up_path_for(resource)
        pages_googleauthqr_path
    end
end
```

```ruby:config/routes.rb
Rails.application.routes.draw do
  root 'pages#index'
  get 'pages/googleauthqr'　<=ここを追加
  devise_for :users, controllers: { registrations: 'registrations'} <=ここを修正
end
```

```ruby:app/views/pages/googleauthqr.html.erb
<h2>Google Authentificator QR code</h2>
<% if current_user %>
  <p>emal : <%= current_user.email %></p>
  <%= raw RQRCode::render_qrcode(current_user.otp_provisioning_uri(current_user.email, issuer: "Your App"),
                                 :svg,
                                 :level => :l,
                                 :unit => 2) %>
<% end %>
```

ここまで書いて一旦お休み。気が向いたら続きを書きます。