Railsアプリにdevise-two-factorとrqrcodeを使って2段階認証を導入した
こんにちは!kossyです!
最近はオリジナルアプリの実装に熱を入れ過ぎてブログの更新が疎かになっておりました、、、
アウトプットが大事だと頭でわかってはいるものの、
実行に移すのは大変ですね、、、
さて、今回はRailsアプリに2段階認証を導入する手順について、
ブログに残してみたいと思います。
環境
Rails 5.2.2
Ruby 2.5.1
devise 4.6.1
devise_two_factor 3.0.3
rqrcode 0.10.1
dotenv-rails 2.6.0
google_authenticator
macOs Mojave
[注意]
この解説記事内でgoogleの2段階認証アプリを使うので、実際に導入してみたい方は
お手持ちのスマートフォンにGoogle Authenticatorを入れて下さい。
iOS: 「Google Authenticator」をApp Storeで
Android: Google 認証システムのインストール - Android - Google アカウント ヘルプ
Gemの導入
まずはGemの導入からです。
./Gemfile # groupのブロック外ならどこに記述してもOKです gem 'devise' gem 'devise-two-factor' gem 'rqrcode' gem 'dotenv-rails' # ターミナル $ bundle install
それぞれ端的に解説すると、
deviseは言わずと知れた、認証の仕組みを数コマンドで提供してくれるGemです。
devise_two_factorを使うと、簡単に2段階認証の仕組みをRailsアプリに導入することができます。
rqrcodeは、QRコードの生成を行うgemです。
dotenv-railsは環境変数の管理をしてくれるGemです。
deviseの導入
deviseの導入は細かい説明は省きます。
詳しく知りたい方は公式ドキュメントや、
[*Rails*] deviseの使い方(rails5版) - Qiita
こちらを参考にしてみてください。
$ rails g devise:install $ rails g devise:views $ rails g devise User $ rails g devise:controllers users $ rails db:migrate
上記のコマンドを順に実行してください。
次に、ルーティングを追加します。
config/routes.rb Rails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions' } end
これでdeviseの導入は終了です。
次にdevise_two_factorの設定を行います。
devise-two-factorを導入
ターミナルで以下のコマンドを実行します。
$ rails g devise_two_factor User TWO_FACTOR_ENCRYPTION_KEY $ rails db:migrate
このコマンドを実行することで、以下の処理が実行されます。
・app/models/user.rbにdevise_two_factorの設定を追加
・:database_authenticatableを削除しようとする。 ※削除されない場合は手動で削除してください。
・config/initializers/devise.rbにwardenの設定を追加
・db/migrate下に二段階認証関連のカラムを追加するためのマイグレーションファイルを生成
追加されるカラムは以下の5種類です。
- encrypted_otp_secret(string)
- encrypted_otp_secret_iv(string)
- encrypted_otp_secret_salt(string)
- consumed_timestep(integer)
- otp_required_for_login(boolean)
ワンタイムパスワードのパラメータを入れるカラムと、
ユーザーが2段階認証を有効にしているかどうかを判断するためのカラムを追加しています。
次に、環境変数の設定を追加します。
.envファイルの設定
アプリケーションフォルダ直下に.envファイルを作成し、
環境変数を設定します。
./ .env # 例です TWO_FACTOR_ENCRYPTION_KEY=hogehogehogehogehogehogehogehogehogehogehoge
このファイルは.gitignoreに追加して、gitの管理から外れるようにしましょう。
.gitignore .env
次に、application_controller.rbにStrongParametersでotp_attempt(認証コード)を許可するように設定を加えます。
StrongParameterの設定
StrongParameterについては以下の記事が詳しかったです。
Rails初学者がつまずきやすい「ストロングパラメータの仕組み」
app/controllers/application_controller.rb class ApplicationController < ActionController::Base 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]) end end
次項から、2段階認証の仕組みを実装していきます。
2段階認証を実装
Deviseを使っているRailsアプリに2段階認証を導入する - Qiita
こちらの記事のコードを参考に実装させていただきました。
app/controllers/users/sessions_controller.rbを編集し、通常のdeviseの認証の前に処理を挟むようにします。
class Users::SessionsController < Devise::SessionsController prepend_before_action :authenticate_with_two_factor, only: [:create] private def authenticate_with_two_factor # strong parameters user_params = params.require(:user).permit(:email, :password, :remember_me, :otp_attempt) if user_params[:email] user = User.find_by(email: user_params[:email]) elsif user_params[:otp_attempt].present? && session[:otp_user_id] user = User.find(session[:otp_user_id]) end self.resource = user return unless user && user.otp_required_for_login if user_params[:email] if user.valid_password?(user_params[:password]) session[:otp_user_id] = user.id render 'devise/sessions/two_factor' and return end elsif user_params[:otp_attempt].present? && session[:otp_user_id] if user.validate_and_consume_otp!(user_params[:otp_attempt]) session.delete(:otp_user_id) sign_in(user) and return else flash.now[:alert] = 'Invalid two-factor code.' render :two_factor and return end end end end
次に、認証コードの入力画面を作成します。
app/views/devise/sessions/two_factor.html.erb <h2>Log in</h2> <%= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| %> <div class="field"> <%= f.label :otp_attempt %><br /> <%= f.text_field :otp_attempt %> </div> <div class="actions"> <%= f.submit "Verify" %> </div> <% end %>
ルーティングは下記のように実装します。
resource :two_factor_auth, only: [:new, :create, :destroy]
次に、2段階認証周りの機能を提供するtwo_factor_auths_controller.rbを作成します。
app/controllers/two_factor_auths_controller.rb class TwoFactorAuthsController < ApplicationController # 2段階認証有効化確認 def new unless current_user.otp_secret current_user.otp_secret = User.generate_otp_secret(32) current_user.save! end @qr_code = build_qr_code end # 2段階認証有効化 def create if current_user.validate_and_consume_otp!(params[:otp_attempt]) current_user.otp_required_for_login = true current_user.save! redirect_to root_path else @error = 'Invalid pin code' @qr_code = build_qr_code render 'new' end end # 2段階認証無効化 def destroy current_user.update_attributes( otp_required_for_login: false, encrypted_otp_secret: nil, encrypted_otp_secret_iv: nil, encrypted_otp_secret_salt: nil, ) redirect_to root_path end private def build_qr_code label = "service_name" # issuerでGoogle Authenticator上の登録サービス名として表示 issuer ="company_name" uri = current_user.otp_provisioning_uri(label, issuer: issuer) qrcode = RQRCode::QRCode.new(uri) qrcode.as_svg( offset: 0, color: '000', shape_rendering: 'crispEdges', module_size: 2 ) end end
QRコード表示画面を作成
app/views/two_factor_auths/new.html.erb <h1>Enable 2FA</h1> <% if @error %> <div><%= @error %></div> <% end %> <div><%= raw @qr_code %></div> <%= form_tag two_factor_auth_path do %> <div class="field"> <%= label_tag :otp_attempt %><br /> <%= text_field_tag :otp_attempt %> </div> <%= submit_tag :submit %> <% end %>
認証機能の有効化・無効化画面の例
app/views/home/index.html.erb(ユーザープロフィール画面があればそこに表示するのがベスト?) <div> <% if user_signed_in? %> <% if current_user.otp_required_for_login %> <%= button_to "Disable 2FA", two_factor_auth_path, method: :delete %> <% else %> <%= button_to "Enable 2FA", new_two_factor_auth_path, method: :get %> <% end %> <% end %> </div>
これで一通りの実装は完了です。
それでは、ローカルで試してみます。(本ブログでは解説していないcontrollerとrootの設定をしています。)
localhost:3000/users/sign_upにアクセスし、必要情報を入力して、submitを押下します。
ユーザー登録に成功すると、enable 2FAのボタンが表示されるので、クリックします。
すると、QRコードが表示されるので、google認証のアプリでQRコードを読み取り、6桁の認証コードを入力します。
正しく認証をパスすると、Enable 2FAだったボタンが、Disable 2FAの表示に変わっていると思います。
これで2段階認証の導入は終了です。
思った通り動作するのはやっぱり嬉しいですね。
参考にさせていただいた記事
Deviseを使っているRailsアプリに2段階認証を導入する - Qiita
deviseとGoogle Authenticatorを用いてRailsシステムに「二段階認証」を導入した話 - LiBz Tech Blog
Railsの二段階認証Gem Devise-Two-FactorのDemoを触ってみた - Qiita
GitHub - tinfoil/devise-two-factor: Barebones two-factor authentication with Devise