diff --git a/app/controllers/concerns/two_factor_authenticatable.rb b/app/controllers/concerns/two_factor_authenticatable.rb index 2e836d7913f..c4113be0924 100644 --- a/app/controllers/concerns/two_factor_authenticatable.rb +++ b/app/controllers/concerns/two_factor_authenticatable.rb @@ -52,7 +52,7 @@ def handle_valid_otp end def authentication_context? - context == 'authentication' + context == 'authentication' || context == 'reauthentication' end def confirmation_context? @@ -71,7 +71,7 @@ def delivery_method # You can pass in any "type" with a corresponding I18n key in # devise.two_factor_authentication.invalid_#{type} def handle_invalid_otp(type: 'otp') - update_invalid_user if current_user.two_factor_enabled? && context == 'authentication' + update_invalid_user if current_user.two_factor_enabled? && authentication_context? flash.now[:error] = t("devise.two_factor_authentication.invalid_#{type}") @@ -183,7 +183,7 @@ def otp_phone_view_data end def display_phone_to_deliver_to - if context == 'authentication' + if authentication_context? decorated_user.masked_two_factor_phone_number else user_session[:unconfirmed_phone] diff --git a/app/controllers/reauthn_required_controller.rb b/app/controllers/reauthn_required_controller.rb index 0d4ccbd003c..f78f5d840d1 100644 --- a/app/controllers/reauthn_required_controller.rb +++ b/app/controllers/reauthn_required_controller.rb @@ -6,7 +6,7 @@ def confirm_recently_authenticated return unless user_signed_in? return if recently_authenticated? store_location_for(:user, request.url) - user_session[:context] = 'authentication' + user_session[:context] = 'reauthentication' redirect_to user_password_confirm_url end diff --git a/app/controllers/users/two_factor_authentication_controller.rb b/app/controllers/users/two_factor_authentication_controller.rb index 9e5f6c0973b..dc3e1504946 100644 --- a/app/controllers/users/two_factor_authentication_controller.rb +++ b/app/controllers/users/two_factor_authentication_controller.rb @@ -58,7 +58,7 @@ def delivery_params end def phone_to_deliver_to - return current_user.phone if context == 'authentication' + return current_user.phone if authentication_context? user_session[:unconfirmed_phone] end diff --git a/spec/controllers/reauthn_required_controller_spec.rb b/spec/controllers/reauthn_required_controller_spec.rb index 652e55cc8bc..868c29c92e0 100644 --- a/spec/controllers/reauthn_required_controller_spec.rb +++ b/spec/controllers/reauthn_required_controller_spec.rb @@ -39,7 +39,7 @@ def show it 'sets context to authentication' do get :show - expect(controller.user_session[:context]).to eq 'authentication' + expect(controller.user_session[:context]).to eq 'reauthentication' end end end diff --git a/spec/features/two_factor_authentication/change_factor_spec.rb b/spec/features/two_factor_authentication/change_factor_spec.rb index b35837056b6..e30a8d15117 100644 --- a/spec/features/two_factor_authentication/change_factor_spec.rb +++ b/spec/features/two_factor_authentication/change_factor_spec.rb @@ -65,6 +65,31 @@ end end + context 'resending OTP code to old phone' do + it 'resends OTP and prompts user to enter their code' do + allow(SmsOtpSenderJob).to receive(:perform_later) + + user = sign_in_and_2fa_user + old_phone = user.phone + + Timecop.travel(Figaro.env.reauthn_window.to_i + 1) do + visit manage_phone_path + complete_2fa_confirmation_without_entering_otp + click_link t('links.two_factor_authentication.resend_code.sms') + + expect(SmsOtpSenderJob).to have_received(:perform_later). + with( + code: user.reload.direct_otp, + phone: old_phone, + otp_created_at: user.reload.direct_otp_sent_at.to_s + ) + + expect(current_path). + to eq login_two_factor_path(delivery_method: 'sms') + end + end + end + scenario 'editing email' do visit manage_email_path complete_2fa_confirmation