はじめに
以前の記事で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も入れておきます。
# 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
# 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
を実行します。
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の自動修正はされませんでしたので手で修正します。
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を使う場合はパスワードリセット後の自動ログインをさせない。
# 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} のようにしてください。
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コード生成画面に飛ばす。
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
pages_googleauthqr_path
end
end
Rails.application.routes.draw do
root 'pages#index'
get 'pages/googleauthqr' <=ここを追加
devise_for :users, controllers: { registrations: 'registrations'} <=ここを修正
end
<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 %>
ここまで書いて一旦お休み。気が向いたら続きを書きます。