Commit 32971b0a authored by Robert Speicher's avatar Robert Speicher

Refactor SessionsController

Also adds test case for providing an invalid 2FA code and then a valid
one without re-entering username and password.
parent 6369d23d
...@@ -29,10 +29,7 @@ class SessionsController < Devise::SessionsController ...@@ -29,10 +29,7 @@ class SessionsController < Devise::SessionsController
def create def create
super do |resource| super do |resource|
# Remove any lingering user data from login # User has successfully signed in, so clear any unused reset token
session.delete(:user)
# User has successfully signed in, so clear any unused reset tokens
if resource.reset_password_token.present? if resource.reset_password_token.present?
resource.update_attributes(reset_password_token: nil, resource.update_attributes(reset_password_token: nil,
reset_password_sent_at: nil) reset_password_sent_at: nil)
...@@ -46,34 +43,40 @@ class SessionsController < Devise::SessionsController ...@@ -46,34 +43,40 @@ class SessionsController < Devise::SessionsController
params.require(:user).permit(:login, :password, :remember_me, :otp_attempt) params.require(:user).permit(:login, :password, :remember_me, :otp_attempt)
end end
def find_user
if user_params[:login]
User.by_login(user_params[:login])
elsif user_params[:otp_attempt] && session[:otp_user_id]
User.find(session[:otp_user_id])
end
end
def authenticate_with_two_factor def authenticate_with_two_factor
@user = User.by_login(user_params[:login]) user = self.resource = find_user
return unless user && user.otp_required_for_login
if user_params[:otp_attempt].present? && session[:user] if user_params[:otp_attempt].present? && session[:otp_user_id]
if valid_otp_attempt? if valid_otp_attempt?(user)
# Insert the saved params from the session into the request parameters # Remove any lingering user data from login
# so they're available to Devise::Strategies::DatabaseAuthenticatable session.delete(:otp_user_id)
request.params[:user].merge!(session[:user])
sign_in(user)
else else
@error = 'Invalid two-factor code' @error = 'Invalid two-factor code'
render :two_factor and return render :two_factor and return
end end
else else
if @user && @user.valid_password?(user_params[:password]) if user && user.valid_password?(user_params[:password])
self.resource = @user # Save the user's ID to session so we can ask for a one-time password
session[:otp_user_id] = user.id
if resource.otp_required_for_login render :two_factor and return
# Login is valid, save the values to the session so we can prompt the
# user for a one-time password.
session[:user] = user_params
render :two_factor and return
end
end end
end end
end end
def valid_otp_attempt? def valid_otp_attempt?(user)
@user.valid_otp?(user_params[:otp_attempt]) || user.valid_otp?(user_params[:otp_attempt]) ||
@user.invalidate_otp_backup_code!(user_params[:otp_attempt]) user.invalidate_otp_backup_code!(user_params[:otp_attempt])
end end
end end
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
- if @error - if @error
.alert.alert-danger .alert.alert-danger
= @error = @error
= f.hidden_field :login
= f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor authentication code', required: true, autofocus: true = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor authentication code', required: true, autofocus: true
.prepend-top-20 .prepend-top-20
= f.submit "Verify code", class: "btn btn-save" = f.submit "Verify code", class: "btn btn-save"
...@@ -31,6 +31,14 @@ feature 'Login' do ...@@ -31,6 +31,14 @@ feature 'Login' do
enter_code('foo') enter_code('foo')
expect(page).to have_content('Invalid two-factor code') expect(page).to have_content('Invalid two-factor code')
end end
it 'allows login with invalid code, then valid code' do
enter_code('foo')
expect(page).to have_content('Invalid two-factor code')
enter_code(user.current_otp)
expect(current_path).to eq root_path
end
end end
context 'using backup code' do context 'using backup code' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment