Commit 2c4c0ad

mo <mo.khan@gmail.com>
2018-09-04 19:42:18
redirect to mfa login page.
1 parent 1c35a9d
app/controllers/application_controller.rb
@@ -4,6 +4,7 @@ class ApplicationController < ActionController::Base
   include SamlRespondable
   protect_from_forgery with: :exception
   before_action :authenticate!
+  before_action :authenticate_mfa!
   helper_method :current_user, :current_user?
   add_flash_types :error, :warning
 
@@ -29,4 +30,9 @@ class ApplicationController < ActionController::Base
   def authenticate!
     redirect_to new_session_path unless current_user?
   end
+
+  def authenticate_mfa!
+    return unless current_user?
+    redirect_to mfa_path unless current_user.tfa.valid_session?(session[:mfa])
+  end
 end
app/controllers/mfas_controller.rb
@@ -1,10 +1,13 @@
 # frozen_string_literal: true
 
 class MfasController < ApplicationController
+  skip_before_action :authenticate_mfa!
+
   def new; end
 
   def create
     if current_user.tfa.authenticate(secure_params[:code])
+      session[:mfa] = { issued_at: Time.now.utc.to_i }
       redirect_to response_path
     else
       redirect_to mfa_path, error: "Invalid code"
app/models/tfa.rb
@@ -35,6 +35,11 @@ class Tfa
     totp.verify(entered_code)
   end
 
+  def valid_session?(session)
+    return true unless setup?
+    session && session[:issued_at].present?
+  end
+
   private
 
   def totp
spec/requests/my/mfas_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe '/my/mfa' do
     describe "GET /my/mfa" do
       context "when MFA is set up" do
         let(:current_user) { create(:user, :mfa_configured) }
+
         before { get '/my/mfa' }
         specify { expect(response).to redirect_to(edit_my_mfa_path) }
       end
spec/requests/mfas_spec.rb
@@ -18,6 +18,7 @@ RSpec.describe "/mfa" do
         before { post '/mfa', params: { mfa: { code: correct_code } } }
 
         specify { expect(response).to redirect_to(response_path) }
+        specify { expect(session[:mfa]).to be_present }
       end
 
       context "when the code is incorrect" do
spec/requests/response_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe "/response" do
     context "when the user has completed password authentication" do
       let(:current_user) { create(:user) }
 
-      before { http_login(current_user) }
+      before { http_login(current_user, skip_mfa: true) }
 
       context "when a saml request was present in session" do
         let(:registry) { Saml::Kit::DefaultRegistry.new }
@@ -54,6 +54,14 @@ RSpec.describe "/response" do
 
         specify { expect(response).to redirect_to(my_dashboard_path) }
       end
+
+      context "when MFA authentication has not been completed" do
+        let(:current_user) { create(:user, :mfa_configured) }
+
+        before { get '/response' }
+
+        specify { expect(response).to redirect_to(mfa_path) }
+      end
     end
   end
 end
spec/support/request.rb
@@ -1,7 +1,13 @@
 RSpec.configure do |config|
   config.include(Module.new do
-    def http_login(user)
+    def http_login(user, skip_mfa: false)
       post '/session', params: { user: { email: user.email, password: user.password } }
+      return if skip_mfa
+      mfa_login(user) if user.tfa.setup?
+    end
+
+    def mfa_login(user)
+      post '/mfa', params: { mfa: { code: user.tfa.current_totp } }
     end
   end)
 end