Refactor SessionsController
Also adds test case for providing an invalid 2FA code and then a valid one without re-entering username and password.
This commit is contained in:
parent
6369d23d58
commit
32971b0af4
|
@ -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
|
||||||
params.require(:user).permit(:login, :password, :remember_me, :otp_attempt)
|
params.require(:user).permit(:login, :password, :remember_me, :otp_attempt)
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_with_two_factor
|
def find_user
|
||||||
@user = User.by_login(user_params[:login])
|
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
|
||||||
|
|
||||||
if user_params[:otp_attempt].present? && session[:user]
|
def authenticate_with_two_factor
|
||||||
if valid_otp_attempt?
|
user = self.resource = find_user
|
||||||
# Insert the saved params from the session into the request parameters
|
|
||||||
# so they're available to Devise::Strategies::DatabaseAuthenticatable
|
return unless user && user.otp_required_for_login
|
||||||
request.params[:user].merge!(session[:user])
|
|
||||||
|
if user_params[:otp_attempt].present? && session[:otp_user_id]
|
||||||
|
if valid_otp_attempt?(user)
|
||||||
|
# Remove any lingering user data from login
|
||||||
|
session.delete(:otp_user_id)
|
||||||
|
|
||||||
|
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 @@
|
||||||
- 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
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue