Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/controllers/concerns/phone_confirmation.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module PhoneConfirmation
def prompt_to_confirm_phone(phone:, context: 'confirmation', selected_delivery_method: nil)
def prompt_to_confirm_phone(phone:, selected_delivery_method: nil)
user_session[:unconfirmed_phone] = phone
user_session[:context] = context
user_session[:context] = 'confirmation'

redirect_to otp_send_url(
otp_delivery_selection_form: {
Expand Down
1 change: 0 additions & 1 deletion app/controllers/concerns/remember_device_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module RememberDeviceConcern
extend ActiveSupport::Concern

def save_remember_device_preference
return if idv_context?
return unless params[:remember_device] == 'true'
cookies.encrypted[:remember_device] = {
value: RememberDeviceCookie.new(user_id: current_user.id, created_at: Time.zone.now).to_json,
Expand Down
51 changes: 11 additions & 40 deletions app/controllers/concerns/two_factor_authenticatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def reset_attempt_count_if_user_no_longer_locked_out
def handle_valid_otp
if authentication_context?
handle_valid_otp_for_authentication_context
elsif idv_or_confirmation_context? || profile_context?
elsif confirmation_context?
handle_valid_otp_for_confirmation_context
end
save_remember_device_preference
Expand Down Expand Up @@ -128,7 +128,7 @@ def handle_valid_otp_for_authentication_context
end

def assign_phone
@updating_existing_number = old_phone.present? && !profile_context?
@updating_existing_number = old_phone.present?

if @updating_existing_number && confirmation_context?
phone_changed
Expand All @@ -153,32 +153,10 @@ def phone_confirmed
end

def update_phone_attributes
if idv_or_profile_context?
update_idv_state
else
UpdateUser.new(
user: current_user,
attributes: { phone: user_session[:unconfirmed_phone], phone_confirmed_at: Time.zone.now }
).call
end
end

def update_idv_state
if idv_context?
confirm_idv_session_phone
elsif profile_context?
Idv::ProfileActivator.new(user: current_user).call
end
end

def confirm_idv_session_phone
idv_session = Idv::Session.new(
user_session: user_session,
current_user: current_user,
issuer: sp_session[:issuer]
)
idv_session.user_phone_confirmation = true
idv_session.params['phone_confirmed_at'] = Time.zone.now
UpdateUser.new(
user: current_user,
attributes: { phone: user_session[:unconfirmed_phone], phone_confirmed_at: Time.zone.now }
).call
end

def reset_otp_session_data
Expand All @@ -187,9 +165,7 @@ def reset_otp_session_data
end

def after_otp_verification_confirmation_url
if idv_context?
idv_review_url
elsif after_otp_action_required?
if after_otp_action_required?
after_otp_action_url
else
after_sign_in_path_for(current_user)
Expand Down Expand Up @@ -224,28 +200,25 @@ def direct_otp_code
end

def personal_key_unavailable?
idv_or_confirmation_context? ||
profile_context? ||
current_user.encrypted_recovery_code_digest.blank?
current_user.encrypted_recovery_code_digest.blank?
end

def unconfirmed_phone?
user_session[:unconfirmed_phone] && idv_or_confirmation_context?
user_session[:unconfirmed_phone] && confirmation_context?
end

# rubocop:disable MethodLength
def phone_view_data
{
confirmation_for_phone_change: confirmation_for_phone_change?,
confirmation_for_idv: idv_context?,
phone_number: display_phone_to_deliver_to,
code_value: direct_otp_code,
otp_delivery_preference: two_factor_authentication_method,
voice_otp_delivery_unsupported: voice_otp_delivery_unsupported?,
reenter_phone_number_path: reenter_phone_number_path,
unconfirmed_phone: unconfirmed_phone?,
totp_enabled: current_user.totp_enabled?,
remember_device_available: !idv_context?,
remember_device_available: true,
account_reset_token: account_reset_token,
}.merge(generic_data)
end
Expand Down Expand Up @@ -295,9 +268,7 @@ def decorated_user

def reenter_phone_number_path
locale = LinkLocaleResolver.locale
if idv_context?
idv_phone_path(locale: locale)
elsif current_user.phone.present?
if current_user.phone.present?
manage_phone_path(locale: locale)
else
phone_setup_path(locale: locale)
Expand Down
16 changes: 0 additions & 16 deletions app/controllers/concerns/user_session_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,4 @@ def authentication_context?
def confirmation_context?
context == 'confirmation'
end

def idv_context?
context == 'idv'
end

def idv_or_confirmation_context?
confirmation_context? || idv_context?
end

def idv_or_profile_context?
idv_context? || profile_context?
end

def profile_context?
context == 'profile'
end
end
1 change: 0 additions & 1 deletion app/controllers/concerns/verify_profile_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ def account_or_verify_profile_url
end

def account_or_verify_profile_route
return 'account' if idv_context? || profile_context?
return 'account' unless profile_needs_verification?
'verify_account'
end
Expand Down
14 changes: 8 additions & 6 deletions app/controllers/idv/otp_delivery_method_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Idv
class OtpDeliveryMethodController < ApplicationController
include IdvSession
include PhoneConfirmation

before_action :confirm_two_factor_authenticated
before_action :confirm_phone_step_complete
before_action :confirm_step_needed
before_action :set_otp_delivery_method_presenter
Expand All @@ -13,11 +13,8 @@ def new; end
def create
result = @otp_delivery_selection_form.submit(otp_delivery_selection_params)
if result.success?
prompt_to_confirm_phone(
phone: @otp_delivery_selection_form.phone,
context: 'idv',
selected_delivery_method: @otp_delivery_selection_form.otp_delivery_preference
)
save_delivery_preference_in_session
redirect_to idv_send_phone_otp_url
else
render :new
end
Expand Down Expand Up @@ -53,5 +50,10 @@ def set_otp_delivery_selection_form
'idv'
)
end

def save_delivery_preference_in_session
idv_session.phone_confirmation_otp_delivery_method =
@otp_delivery_selection_form.otp_delivery_preference
end
end
end
113 changes: 113 additions & 0 deletions app/controllers/idv/otp_verification_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
module Idv
class OtpVerificationController < ApplicationController
include IdvSession
include TwoFactorAuthenticatable

# Skip TwoFactorAuthenticatable before action that depends on MFA contexts
# to redirect to account url for signed in users. This controller does not
# use or maintain MFA contexts
skip_before_action :check_already_authenticated

before_action :confirm_two_factor_authenticated
before_action :confirm_step_needed
before_action :handle_locked_out_user
before_action :confirm_otp_delivery_preference_selected
before_action :confirm_otp_sent, only: %i[show update]
before_action :set_code, only: %i[show update]
before_action :set_otp_verification_presenter, only: %i[show update]

def new
result = send_phone_confirmation_otp_form.submit
analytics.track_event(Analytics::IDV_PHONE_CONFIRMATION_OTP_SENT, result.to_h)
if result.success?
redirect_to idv_otp_verification_url
elsif send_phone_confirmation_otp_form.user_locked_out?
handle_too_many_otp_sends
else
redirect_to idv_otp_delivery_method_url
end
end

def show
# memoize the form so the ivar is available to the view
phone_confirmation_otp_verification_form
analytics.track_event(Analytics::IDV_PHONE_CONFIRMATION_OTP_VISIT)
end

def update
result = phone_confirmation_otp_verification_form.submit(code: params[:code])
analytics.track_event(Analytics::IDV_PHONE_CONFIRMATION_OTP_SUBMITTED, result.to_h)
if result.success?
redirect_to idv_review_url
else
handle_otp_confirmation_failure
end
end

private

def confirm_step_needed
return unless idv_session.user_phone_confirmation
redirect_to idv_review_url
end

def handle_locked_out_user
reset_attempt_count_if_user_no_longer_locked_out
return unless decorated_user.locked_out?
handle_second_factor_locked_user 'generic'
false
end

def confirm_otp_delivery_preference_selected
return if idv_session.params[:phone].present? &&
idv_session.phone_confirmation_otp_delivery_method.present?

redirect_to idv_otp_delivery_method_url
end

def confirm_otp_sent
return if idv_session.phone_confirmation_otp.present? &&
idv_session.phone_confirmation_otp_sent_at.present?

redirect_to idv_send_phone_otp_url
end

def set_code
return unless FeatureManagement.prefill_otp_codes?
@code = idv_session.phone_confirmation_otp
end

def set_otp_verification_presenter
@presenter = OtpVerificationPresenter.new(idv_session: idv_session)
end

def handle_otp_confirmation_failure
if decorated_user.locked_out?
handle_second_factor_locked_user('otp')
else
flash.now[:error] = t('devise.two_factor_authentication.invalid_otp')
render :show
end
end

def send_phone_confirmation_otp_form
@send_phone_confirmation_otp_form ||= SendPhoneConfirmationOtpForm.new(
user: current_user,
idv_session: idv_session,
locale: user_locale
)
end

def phone_confirmation_otp_verification_form
@phone_confirmation_otp_verification_form ||= PhoneConfirmationOtpVerificationForm.new(
user: current_user,
idv_session: idv_session
)
end

def user_locale
available_locales = PhoneVerification::AVAILABLE_LOCALES
http_accept_language.language_region_compatible_from(available_locales)
end
end
end
6 changes: 1 addition & 5 deletions app/controllers/idv/review_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ def confirm_idv_steps_complete
def confirm_idv_phone_confirmed
return unless idv_session.address_verification_mechanism == 'phone'
return if idv_session.phone_confirmed?

prompt_to_confirm_phone(
phone: idv_session.params[:phone],
context: 'idv'
)
redirect_to idv_otp_verification_path
end

def confirm_current_password
Expand Down
1 change: 0 additions & 1 deletion app/controllers/idv/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class SessionsController < ApplicationController
delegate :attempts_exceeded?, to: :step, prefix: true

def new
user_session[:context] = 'idv'
set_idv_form
@selected_state = user_session[:idv_jurisdiction]
analytics.track_event(Analytics::IDV_BASIC_INFO_VISIT)
Expand Down
1 change: 1 addition & 0 deletions app/controllers/openid_connect/authorization_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def redirect_to_account_or_verify_profile_url
end

def profile_or_identity_needs_verification?
return false unless @authorize_form.loa3_requested?
profile_needs_verification? || identity_needs_verification?
end

Expand Down
1 change: 1 addition & 0 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def redirect_to_account_or_verify_profile_url
end

def profile_or_identity_needs_verification?
return false unless loa3_requested?
profile_needs_verification? || identity_needs_verification?
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def create
private

def confirm_two_factor_enabled
return if confirming_phone? || phone_enabled?
return if confirmation_context? || phone_enabled?

if current_user.two_factor_enabled? && !phone_enabled? && user_signed_in?
return redirect_to user_two_factor_authentication_url
Expand All @@ -35,10 +35,6 @@ def confirm_two_factor_enabled
redirect_to phone_setup_url
end

def confirming_phone?
idv_context? || confirmation_context?
end

def phone_enabled?
current_user.phone_enabled?
end
Expand Down
Loading