From 24936bddedb8e3350262e4062da610a91ea2c2f4 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 2 Apr 2025 11:53:57 -0400 Subject: [PATCH 01/13] add address errors page --- .../idv/session_errors_controller.rb | 12 ++++ .../session_errors/address_warning.html.erb | 26 ++++++++ config/locales/en.yml | 2 + config/locales/es.yml | 2 + config/locales/fr.yml | 2 + config/locales/zh.yml | 2 + config/routes.rb | 1 + .../idv/session_errors_controller_spec.rb | 62 +++++++++++++++++++ 8 files changed, 109 insertions(+) create mode 100644 app/views/idv/session_errors/address_warning.html.erb diff --git a/app/controllers/idv/session_errors_controller.rb b/app/controllers/idv/session_errors_controller.rb index 129ab660980..a55ee3b2915 100644 --- a/app/controllers/idv/session_errors_controller.rb +++ b/app/controllers/idv/session_errors_controller.rb @@ -30,6 +30,18 @@ def state_id_warning log_event end + def address_warning + rate_limiter = RateLimiter.new( + user: idv_session_user, + rate_limit_type: :idv_resolution, + ) + + @step_indicator_steps = step_indicator_steps + @address_path = idv_address_url + @remaining_submit_attempts = rate_limiter.remaining_count + log_event(based_on_limiter: rate_limiter) + end + def failure rate_limiter = RateLimiter.new( user: idv_session_user, diff --git a/app/views/idv/session_errors/address_warning.html.erb b/app/views/idv/session_errors/address_warning.html.erb new file mode 100644 index 00000000000..0578ccc848c --- /dev/null +++ b/app/views/idv/session_errors/address_warning.html.erb @@ -0,0 +1,26 @@ +<% self.title = t('titles.failure.information_not_verified') %> + +<% content_for(:pre_flash_content) do %> + <%= render StepIndicatorComponent.new( + steps: @step_indicator_steps, + current_step: :verify_info, + locale_scope: 'idv', + class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4', + ) %> +<% end %> + +<%= render StatusPageComponent.new(status: :warning) do |c| %> + <% c.with_header { t('idv.warning.address.heading') } %> + +

<%= t('idv.failure.address.warning') %>

+

<%= t('idv.failure.attempts_html', count: @remaining_submit_attempts) %>

+ + <% c.with_action_button( + url: @address_path, + class: 'margin-bottom-2', + ) { t('idv.forgot_password.try_again') } %> +<% end %> + +<%= render PageFooterComponent.new do %> + <%= link_to t('links.cancel'), idv_cancel_path(step: :invalid_session) %> +<% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 2d28ebe903c..5a64a5470e3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1062,6 +1062,7 @@ idv.errors.pattern_mismatch.zipcode: Enter a 5 or 9 digit ZIP Code idv.errors.pattern_mismatch.zipcode_five: Enter a 5 digit ZIP Code idv.errors.technical_difficulties: We are having technical difficulties idv.errors.try_again_later: Try again later. +idv.failure.address.warning: Please check the information you entered and try again. If your mailing address is different than your residential address, try entering the address where you currently live. idv.failure.attempts_html.one: You can try 1 more time. Then you must wait 6 hours before trying again. idv.failure.attempts_html.other: You can try %{count} more times. Then you must wait 6 hours before trying again. idv.failure.button.try_online: Try again online @@ -1206,6 +1207,7 @@ idv.unavailable.idv_explanation.without_sp: The agency that you are trying to ac idv.unavailable.next_steps_html: '%{status_page_link_html} or exit %{app_name} and try again later.' idv.unavailable.status_page_link: Get updates on our status page idv.unavailable.technical_difficulties: Unfortunately, we are having technical difficulties and cannot verify your identity at this time. +idv.warning.address.heading: We couldn’t find records matching your address idv.warning.sessions.heading: We couldn’t find records matching your personal information idv.warning.state_id.cancel_button: Exit %{app_name} idv.warning.state_id.explanation: Unfortunately, we’re experiencing technical difficulties with IDs from your state and are currently unable to verify your information. diff --git a/config/locales/es.yml b/config/locales/es.yml index 69a76bea1ed..baa8d4f1fff 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1073,6 +1073,7 @@ idv.errors.pattern_mismatch.zipcode: Ingrese un código postal de 5 o 9 dígitos idv.errors.pattern_mismatch.zipcode_five: Ingrese un código postal de 5 dígitos. idv.errors.technical_difficulties: Estamos teniendo problemas técnicos idv.errors.try_again_later: Vuelva a intentarlo más tarde. +idv.failure.address.warning: Revise la información que ingresó y vuelva a intentarlo. Si su dirección postal no es igual que su domicilio, intente ingresar la dirección donde vive actualmente. idv.failure.attempts_html.one: Puede intentarlo una vez más. Luego debe esperar 6 horas antes de volver a intentarlo. idv.failure.attempts_html.other: Puede intentarlo %{count} veces más. Luego debe esperar 6 horas antes de volver a intentarlo. idv.failure.button.try_online: Vuelva a intentarlo en línea @@ -1217,6 +1218,7 @@ idv.unavailable.idv_explanation.without_sp: La agencia a la que está intentando idv.unavailable.next_steps_html: '%{status_page_link_html} o salga de %{app_name} y vuelva a intentarlo más tarde.' idv.unavailable.status_page_link: Obtenga las actualizaciones en nuestra página de estado idv.unavailable.technical_difficulties: Lamentablemente, tenemos problemas técnicos y no podemos verificar su identidad en este momento. +idv.warning.address.heading: No encontramos registros que coincidan con su dirección idv.warning.sessions.heading: No encontramos registros que coincidan con sus datos personales idv.warning.state_id.cancel_button: Salir de %{app_name} idv.warning.state_id.explanation: Lamentablemente, tenemos problemas técnicos con las identificaciones de su estado y no podemos verificar su información en este momento. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 62083c54e82..ccc51df6818 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1062,6 +1062,7 @@ idv.errors.pattern_mismatch.zipcode: Saisissez un code postal à 5 ou 9 chiffres idv.errors.pattern_mismatch.zipcode_five: Saisissez un code postal à 5 chiffres idv.errors.technical_difficulties: Nous rencontrons des difficultés techniques idv.errors.try_again_later: Veuillez réessayer ultérieurement. +idv.failure.address.warning: Veuillez vérifier les informations que vous avez saisies et réessayer. Si votre adresse postale est différente de votre adresse personnelle, veuillez saisir l’adresse où vous résidez actuellement. idv.failure.attempts_html.one: Vous avez encore un essai. Vous devrez ensuite attendre 6 heures avant de réessayer. idv.failure.attempts_html.other: Vous pouvez encore essayer %{count} fois de plus. Ensuite vous devrez ensuite attendre 6 heures avant de réessayer. idv.failure.button.try_online: Réessayer en ligne @@ -1206,6 +1207,7 @@ idv.unavailable.idv_explanation.without_sp: L’organisme auquel vous essayez d idv.unavailable.next_steps_html: '%{status_page_link_html} ou quittez le site %{app_name} et réessayez plus tard.' idv.unavailable.status_page_link: Obtenir les dernières informations sur notre page d’état des systèmes. idv.unavailable.technical_difficulties: Malheureusement, nous rencontrons des difficultés techniques et ne pouvons pas vérifier votre identité pour le moment. +idv.warning.address.heading: Nous n’avons pas trouvé d’informations correspondant à votre adresse idv.warning.sessions.heading: Nous n’avons pas trouvé de dossiers correspondant à vos informations personnelles idv.warning.state_id.cancel_button: Quitter %{app_name} idv.warning.state_id.explanation: Malheureusement, nous rencontrons des difficultés techniques avec les pièces d’identité de votre État et ne sommes pas en mesure de vérifier vos informations pour le moment. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 87ac370a2d7..7a77ea6ed2f 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1075,6 +1075,7 @@ idv.errors.pattern_mismatch.zipcode: 输入 5 或 9 位的邮编 idv.errors.pattern_mismatch.zipcode_five: 输入 5 位的邮编 idv.errors.technical_difficulties: 我们目前遇到技术困难 idv.errors.try_again_later: 请稍后再试。 +idv.failure.address.warning: 请检查一下你输入的信息,然后再试一下。如果你的邮寄地址与居住地址不同,请尝试输入你目前居住的地址。 idv.failure.attempts_html.one: 您可以再试1次。 然后您必须等6个小时才能再试。 idv.failure.attempts_html.other: 您可以再试%{count}次。 然后您必须等6个小时才能再试。 idv.failure.button.try_online: 在网上再试一下 @@ -1219,6 +1220,7 @@ idv.unavailable.idv_explanation.without_sp: 你试图访问的机构需要确保 idv.unavailable.next_steps_html: '%{status_page_link_html} 或者退出 %{app_name},稍后再试。' idv.unavailable.status_page_link: 在我们的状态页面获得最新信息。 idv.unavailable.technical_difficulties: 很遗憾,我们这边现在遇到技术困难,目前无法验证你的身份。 +idv.warning.address.heading: 我们找不到与你地址匹配的记录 idv.warning.sessions.heading: 我们找不到与你个人信息匹配的记录 idv.warning.state_id.cancel_button: 退出 %{app_name} idv.warning.state_id.explanation: 遗憾的是,处理来自你所在州的身份证件时我们遇到技术困难,目前无法验证你的信息。 diff --git a/config/routes.rb b/config/routes.rb index c4362f8cb7b..1ca8eff16ad 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -425,6 +425,7 @@ put '/enter_password' => 'enter_password#create' get '/session/errors/warning' => 'session_errors#warning' get '/session/errors/state_id_warning' => 'session_errors#state_id_warning' + get '/session/errors/address_warning' => 'session_errors#address_warning' get '/session/errors/failure' => 'session_errors#failure' get '/session/errors/ssn_failure' => 'session_errors#ssn_failure' get '/session/errors/exception' => 'session_errors#exception' diff --git a/spec/controllers/idv/session_errors_controller_spec.rb b/spec/controllers/idv/session_errors_controller_spec.rb index 3bf0665bb44..bdda32818c1 100644 --- a/spec/controllers/idv/session_errors_controller_spec.rb +++ b/spec/controllers/idv/session_errors_controller_spec.rb @@ -234,6 +234,68 @@ end end + describe '#address_warning' do + let(:action) { :address_warning } + let(:template) { 'idv/session_errors/address_warning' } + + subject(:response) { get action } + it_behaves_like 'an idv session errors controller action' + + context 'with rate limit attempts' do + let(:user) { create(:user) } + + before do + RateLimiter.new(rate_limit_type: :idv_resolution, user: user).increment! + end + + it 'assigns remaining count' do + response + + expect(assigns(:remaining_submit_attempts)).to be_kind_of(Numeric) + end + + it 'assigns URL to try again' do + response + + expect(assigns(:address_path)).to eq(idv_address_url) + end + + it 'logs an event with attempts remaining' do + response + + expect(@analytics).to have_logged_event( + 'IdV: session error visited', + type: action.to_s, + remaining_submit_attempts: IdentityConfig.store.idv_max_attempts - 1, + ) + end + end + + context 'while rate limited' do + let(:user) { create(:user) } + + before do + RateLimiter.new(rate_limit_type: :idv_resolution, user: user).increment_to_limited! + end + + it 'assigns expiration time' do + get action + + expect(assigns(:expires_at)).not_to eq(Time.zone.now) + end + + it 'logs an event with attempts remaining' do + get action + + expect(@analytics).to have_logged_event( + 'IdV: session error visited', + type: 'address_warning', + remaining_submit_attempts: 0, + ) + end + end + end + describe '#failure' do let(:action) { :failure } let(:template) { 'idv/session_errors/failure' } From 0e21062ecf32a99c92cccefdce090194fe858100 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 9 Apr 2025 16:02:24 -0400 Subject: [PATCH 02/13] add address proofing exception redirect --- .../concerns/idv/verify_info_concern.rb | 21 +++++++ app/services/analytics_events.rb | 13 +++++ .../proofing/resolution/result_adjudicator.rb | 4 +- .../idv/verify_info_controller_spec.rb | 58 +++++++++++++++++++ .../address_warning.html.erb_spec.rb | 57 ++++++++++++++++++ 5 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 spec/views/idv/session_errors/address_warning.html.erb_spec.rb diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index f5e0afbdc04..b58873de524 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -89,6 +89,13 @@ def idv_failure(result) :state_id, :mva_exception, ).present? + is_address_exception = result.extra.dig( + :proofing_results, + :context, + :stages, + :residential_address, + :exception, + ).present? is_threatmetrix_exception = result.extra.dig( :proofing_results, :context, @@ -113,6 +120,9 @@ def idv_failure(result) elsif has_exception && is_mva_exception idv_failure_log_warning redirect_to state_id_warning_url + elsif has_exception && is_address_exception + idv_failure_log_address_warning + redirect_to address_warning_url elsif (has_exception && is_threatmetrix_exception) || (!has_exception && resolution_failed) idv_failure_log_warning @@ -147,6 +157,13 @@ def idv_failure_log_error ) end + def idv_failure_log_address_warning + analytics.idv_doc_auth_address_warning_visited( + step_name: STEP_NAME, + remaining_submit_attempts: resolution_rate_limiter.remaining_count, + ) + end + def idv_failure_log_warning analytics.idv_doc_auth_warning_visited( step_name: STEP_NAME, @@ -166,6 +183,10 @@ def state_id_warning_url idv_session_errors_state_id_warning_url(flow: flow_param) end + def address_warning_url + idv_session_errors_address_warning_url(flow: flow_param) + end + def warning_url idv_session_errors_warning_url(flow: flow_param) end diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 0a513410e6e..1569fc64bc7 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -1226,6 +1226,18 @@ def idv_consent_checkbox_toggled(checked:, **extra) ) end + # @param [String] step_name + # @param [Integer] remaining_submit_attempts (previously called "remaining_attempts") + # The user was sent to a warning page during the IDV flow + def idv_doc_auth_address_warning_visited(step_name:, remaining_submit_attempts:, **extra) + track_event( + :idv_doc_auth_address_warning_visited, + step_name: step_name, + remaining_submit_attempts: remaining_submit_attempts, + **extra, + ) + end + # User has consented to share information with document upload and may # view the "hybrid handoff" step next unless "skip_hybrid_handoff" param is true # @param [Boolean] success Whether form validation was successful @@ -2307,6 +2319,7 @@ def idv_doc_auth_verify_visited( ) end + # @param [String] step_name # @param [Integer] remaining_submit_attempts (previously called "remaining_attempts") # The user was sent to a warning page during the IDV flow diff --git a/app/services/proofing/resolution/result_adjudicator.rb b/app/services/proofing/resolution/result_adjudicator.rb index f8850ba1b02..3a8e6cf6360 100644 --- a/app/services/proofing/resolution/result_adjudicator.rb +++ b/app/services/proofing/resolution/result_adjudicator.rb @@ -101,8 +101,8 @@ def device_profiling_result_and_reason end def resolution_result_and_reason - if !residential_resolution_result.success? && same_address_as_id == 'false' && - ipp_enrollment_in_progress + if !residential_resolution_result.success? && (same_address_as_id == 'false' || + ipp_enrollment_in_progress) [false, :fail_resolution_skip_state_id] elsif resolution_result.success? && state_id_result.success? [true, :pass_resolution_and_state_id] diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index 62050d44865..3200e0e5a2c 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -543,6 +543,64 @@ end end + context 'when address proofing results in an exception' do + let(:document_capture_session) do + DocumentCaptureSession.create(user:) + end + let(:success) { false } + let(:errors) { {} } + let(:exception) { nil } + let(:vendor_name) { 'instantverify_placeholder' } + let(:async_state) do + # Here we're trying to match the store to redis -> read from redis flow this data travels + adjudicated_result = Proofing::Resolution::ResultAdjudicator.new( + state_id_result: Proofing::StateIdResult.new(success: true), + phone_finder_result: Proofing::AddressResult.new( + success: success, + errors: {}, + exception: exception, + vendor_name: 'instant_verify_test', + ), + device_profiling_result: Proofing::DdpResult.new(success: true), + ipp_enrollment_in_progress: false, + residential_resolution_result: Proofing::Resolution::Result.new( + success: success, + errors: {}, + exception: 'fake exception', + vendor_name: vendor_name, + ), + resolution_result: Proofing::Resolution::Result.new(success: true), + same_address_as_id: 'false', + should_proof_state_id: true, + applicant_pii: Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN, + ).adjudicated_result.to_h + + document_capture_session.create_proofing_session + + document_capture_session.store_proofing_result(adjudicated_result) + + document_capture_session.load_proofing_result + end + before do + allow(controller).to receive(:load_async_state).and_return(async_state) + end + + it 'redirects user to address warning' do + put :show + expect(response).to redirect_to idv_session_errors_address_warning_url + end + + it 'logs an event' do + get :show + + expect(@analytics).to have_logged_event( + :idv_doc_auth_address_warning_visited, + step_name: 'verify_info', + remaining_submit_attempts: kind_of(Numeric), + ) + end + end + context 'when the resolution proofing job fails and there is no exception' do before do allow(controller).to receive(:load_async_state).and_return(async_state) diff --git a/spec/views/idv/session_errors/address_warning.html.erb_spec.rb b/spec/views/idv/session_errors/address_warning.html.erb_spec.rb new file mode 100644 index 00000000000..1db17993a6a --- /dev/null +++ b/spec/views/idv/session_errors/address_warning.html.erb_spec.rb @@ -0,0 +1,57 @@ +require 'rails_helper' + +RSpec.describe 'idv/session_errors/address_warning.html.erb' do + let(:sp_name) { nil } + let(:address_path) { '/example/path' } + let(:remaining_submit_attempts) { 5 } + let(:user_session) { {} } + + before do + decorated_sp_session = instance_double(ServiceProviderSession, sp_name: sp_name) + allow(view).to receive(:decorated_sp_session).and_return(decorated_sp_session) + allow(view).to receive(:user_session).and_return(user_session) + + assign(:remaining_submit_attempts, remaining_submit_attempts) + assign(:address_path, address_path) + + @step_indicator_steps = Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS + + render + end + + it 'shows a primary action' do + expect(rendered).to have_link(t('idv.failure.button.warning'), href: address_path) + end + + it 'shows remaining attempts' do + expect(rendered).to have_text( + strip_tags( + t('idv.failure.attempts_html', count: remaining_submit_attempts), + ), + ) + end + + it 'shows a cancel link' do + expect(rendered).to have_link( + t('links.cancel'), + href: idv_cancel_path(step: :invalid_session), + ) + end + + context 'with a nil user_session' do + let(:user_session) { nil } + + it 'does not render troubleshooting option to retake photos' do + expect(rendered).to have_link(t('idv.failure.button.warning'), href: address_path) + expect(rendered).to have_text( + strip_tags( + t('idv.failure.attempts_html', count: remaining_submit_attempts), + ), + ) + expect(rendered).to have_link( + t('links.cancel'), + href: idv_cancel_path(step: :invalid_session), + ) + end + end +end From eb5bb803dfd05de70e797a3fe869865e65d060a9 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 9 Apr 2025 16:15:15 -0400 Subject: [PATCH 03/13] add changelog changelog: User-Facing Improvements, Doc Auth, add we couldnt find your address screen From c50bb21af789043c4ea6688d5383b4659605860f Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 9 Apr 2025 17:45:09 -0400 Subject: [PATCH 04/13] linty mclinterson --- app/services/analytics_events.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb index 1569fc64bc7..96cf10d4c4c 100644 --- a/app/services/analytics_events.rb +++ b/app/services/analytics_events.rb @@ -2319,7 +2319,6 @@ def idv_doc_auth_verify_visited( ) end - # @param [String] step_name # @param [Integer] remaining_submit_attempts (previously called "remaining_attempts") # The user was sent to a warning page during the IDV flow From af96afcc96b70d242a3e3e29b1d8f08997d04011 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Thu, 10 Apr 2025 15:01:26 -0400 Subject: [PATCH 05/13] refactor rate limiter code in session_errors_controller --- .../idv/session_errors_controller.rb | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/app/controllers/idv/session_errors_controller.rb b/app/controllers/idv/session_errors_controller.rb index a55ee3b2915..c15504c502d 100644 --- a/app/controllers/idv/session_errors_controller.rb +++ b/app/controllers/idv/session_errors_controller.rb @@ -8,19 +8,17 @@ class SessionErrorsController < ApplicationController before_action :confirm_two_factor_authenticated_or_user_id_in_session before_action :confirm_idv_session_step_needed + before_action :set_rate_limiter, only: [:warning, :address_warning, :failure] before_action :set_try_again_path, only: [:warning, :exception, :state_id_warning] before_action :ignore_form_step_wait_requests + attr_reader :rate_limiter + def exception log_event end def warning - rate_limiter = RateLimiter.new( - user: idv_session_user, - rate_limit_type: :idv_resolution, - ) - @step_indicator_steps = step_indicator_steps @remaining_submit_attempts = rate_limiter.remaining_count log_event(based_on_limiter: rate_limiter) @@ -31,11 +29,6 @@ def state_id_warning end def address_warning - rate_limiter = RateLimiter.new( - user: idv_session_user, - rate_limit_type: :idv_resolution, - ) - @step_indicator_steps = step_indicator_steps @address_path = idv_address_url @remaining_submit_attempts = rate_limiter.remaining_count @@ -43,10 +36,6 @@ def address_warning end def failure - rate_limiter = RateLimiter.new( - user: idv_session_user, - rate_limit_type: :idv_resolution, - ) @expires_at = rate_limiter.expires_at @sp_name = decorated_sp_session.sp_name log_event(based_on_limiter: rate_limiter) @@ -75,6 +64,13 @@ def rate_limited private + def set_rate_limiter + @rate_limiter = RateLimiter.new( + user: idv_session_user, + rate_limit_type: :idv_resolution, + ) + end + def confirm_two_factor_authenticated_or_user_id_in_session return if session[:doc_capture_user_id].present? From 464440676becb5492f261a0eacd27f648b02a96a Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Fri, 11 Apr 2025 11:54:13 -0400 Subject: [PATCH 06/13] rename rate limiter to resolution_rate_limiter --- .../idv/session_errors_controller.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/idv/session_errors_controller.rb b/app/controllers/idv/session_errors_controller.rb index c15504c502d..c3b04c213ea 100644 --- a/app/controllers/idv/session_errors_controller.rb +++ b/app/controllers/idv/session_errors_controller.rb @@ -8,11 +8,11 @@ class SessionErrorsController < ApplicationController before_action :confirm_two_factor_authenticated_or_user_id_in_session before_action :confirm_idv_session_step_needed - before_action :set_rate_limiter, only: [:warning, :address_warning, :failure] + before_action :set_resolution_rate_limiter, only: [:warning, :address_warning, :failure] before_action :set_try_again_path, only: [:warning, :exception, :state_id_warning] before_action :ignore_form_step_wait_requests - attr_reader :rate_limiter + attr_reader :resolution_rate_limiter def exception log_event @@ -20,8 +20,8 @@ def exception def warning @step_indicator_steps = step_indicator_steps - @remaining_submit_attempts = rate_limiter.remaining_count - log_event(based_on_limiter: rate_limiter) + @remaining_submit_attempts = resolution_rate_limiter.remaining_count + log_event(based_on_limiter: resolution_rate_limiter) end def state_id_warning @@ -31,14 +31,14 @@ def state_id_warning def address_warning @step_indicator_steps = step_indicator_steps @address_path = idv_address_url - @remaining_submit_attempts = rate_limiter.remaining_count - log_event(based_on_limiter: rate_limiter) + @remaining_submit_attempts = resolution_rate_limiter.remaining_count + log_event(based_on_limiter: resolution_rate_limiter) end def failure - @expires_at = rate_limiter.expires_at + @expires_at = resolution_rate_limiter.expires_at @sp_name = decorated_sp_session.sp_name - log_event(based_on_limiter: rate_limiter) + log_event(based_on_limiter: resolution_rate_limiter) end def ssn_failure @@ -64,8 +64,8 @@ def rate_limited private - def set_rate_limiter - @rate_limiter = RateLimiter.new( + def set_resolution_rate_limiter + @resolution_rate_limiter = RateLimiter.new( user: idv_session_user, rate_limit_type: :idv_resolution, ) From 594bcc47d7f4dfdfe9e0531f28080bebdf169f86 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Tue, 15 Apr 2025 15:14:51 -0400 Subject: [PATCH 07/13] changed to resolution result instead of residential_resolution_result --- app/controllers/concerns/idv/verify_info_concern.rb | 2 +- app/services/proofing/resolution/result_adjudicator.rb | 4 ++-- spec/controllers/idv/verify_info_controller_spec.rb | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index b58873de524..5dce8448d2a 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -93,7 +93,7 @@ def idv_failure(result) :proofing_results, :context, :stages, - :residential_address, + :resolution, :exception, ).present? is_threatmetrix_exception = result.extra.dig( diff --git a/app/services/proofing/resolution/result_adjudicator.rb b/app/services/proofing/resolution/result_adjudicator.rb index 3a8e6cf6360..f8850ba1b02 100644 --- a/app/services/proofing/resolution/result_adjudicator.rb +++ b/app/services/proofing/resolution/result_adjudicator.rb @@ -101,8 +101,8 @@ def device_profiling_result_and_reason end def resolution_result_and_reason - if !residential_resolution_result.success? && (same_address_as_id == 'false' || - ipp_enrollment_in_progress) + if !residential_resolution_result.success? && same_address_as_id == 'false' && + ipp_enrollment_in_progress [false, :fail_resolution_skip_state_id] elsif resolution_result.success? && state_id_result.success? [true, :pass_resolution_and_state_id] diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index 3200e0e5a2c..e0071e35187 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -563,14 +563,14 @@ ), device_profiling_result: Proofing::DdpResult.new(success: true), ipp_enrollment_in_progress: false, - residential_resolution_result: Proofing::Resolution::Result.new( + residential_resolution_result: Proofing::Resolution::Result.new(success: true), + resolution_result: Proofing::Resolution::Result.new( success: success, errors: {}, exception: 'fake exception', vendor_name: vendor_name, ), - resolution_result: Proofing::Resolution::Result.new(success: true), - same_address_as_id: 'false', + same_address_as_id: nil, should_proof_state_id: true, applicant_pii: Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN, ).adjudicated_result.to_h From 71eb49ab4087fe3a708824b7dcaed5a4f5279c08 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 16 Apr 2025 15:27:22 -0400 Subject: [PATCH 08/13] add address_exception to resolution result --- app/controllers/concerns/idv/verify_info_concern.rb | 2 +- app/services/proofing/resolution/result.rb | 5 +++++ spec/controllers/idv/verify_info_controller_spec.rb | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 5dce8448d2a..e231398bf9e 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -94,7 +94,7 @@ def idv_failure(result) :context, :stages, :resolution, - :exception, + :address_exception, ).present? is_threatmetrix_exception = result.extra.dig( :proofing_results, diff --git a/app/services/proofing/resolution/result.rb b/app/services/proofing/resolution/result.rb index 0978ce995bb..886f5472f35 100644 --- a/app/services/proofing/resolution/result.rb +++ b/app/services/proofing/resolution/result.rb @@ -47,11 +47,16 @@ def timed_out? exception.is_a?(Proofing::TimeoutError) end + def address_exception? + attributes_requiring_additional_verification == 'address' + end + def to_h { success: success?, errors: errors, exception: exception, + address_exception: address_exception?, timed_out: timed_out?, transaction_id: transaction_id, reference: reference, diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index e0071e35187..b7351e61d28 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -569,6 +569,7 @@ errors: {}, exception: 'fake exception', vendor_name: vendor_name, + attributes_requiring_additional_verification: 'address', ), same_address_as_id: nil, should_proof_state_id: true, From c6371f8e5875993aadcdd638ac77c50fe643e1b6 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 16 Apr 2025 16:01:54 -0400 Subject: [PATCH 09/13] move address fail check to verify_info_concern --- .../concerns/idv/verify_info_concern.rb | 25 +++++++++++++------ app/services/proofing/resolution/result.rb | 5 ---- .../idv/verify_info_controller_spec.rb | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index e231398bf9e..a623c5d42da 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -79,6 +79,23 @@ def ssn_rate_limiter ) end + def is_address_exception?(result) + result.extra.dig( + :proofing_results, + :context, + :stages, + :resolution, + :exception, + ).present? && + result.extra.dig( + :proofing_results, + :context, + :stages, + :resolution, + :attributes_requiring_additional_verification, + ).include?('address') + end + def idv_failure(result) proofing_results_exception = result.extra.dig(:proofing_results, :exception) has_exception = proofing_results_exception.present? @@ -89,13 +106,7 @@ def idv_failure(result) :state_id, :mva_exception, ).present? - is_address_exception = result.extra.dig( - :proofing_results, - :context, - :stages, - :resolution, - :address_exception, - ).present? + is_address_exception = is_address_exception?(result) is_threatmetrix_exception = result.extra.dig( :proofing_results, :context, diff --git a/app/services/proofing/resolution/result.rb b/app/services/proofing/resolution/result.rb index 886f5472f35..0978ce995bb 100644 --- a/app/services/proofing/resolution/result.rb +++ b/app/services/proofing/resolution/result.rb @@ -47,16 +47,11 @@ def timed_out? exception.is_a?(Proofing::TimeoutError) end - def address_exception? - attributes_requiring_additional_verification == 'address' - end - def to_h { success: success?, errors: errors, exception: exception, - address_exception: address_exception?, timed_out: timed_out?, transaction_id: transaction_id, reference: reference, diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index b7351e61d28..d24c427ef22 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -569,7 +569,7 @@ errors: {}, exception: 'fake exception', vendor_name: vendor_name, - attributes_requiring_additional_verification: 'address', + attributes_requiring_additional_verification: ['address'], ), same_address_as_id: nil, should_proof_state_id: true, From ed2e111c9b4c289ade3343a80c7814d87d561fa0 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Wed, 16 Apr 2025 16:31:56 -0400 Subject: [PATCH 10/13] linty mclinterson --- app/controllers/concerns/idv/verify_info_concern.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index a623c5d42da..5fb46f681f9 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -79,7 +79,7 @@ def ssn_rate_limiter ) end - def is_address_exception?(result) + def address_exception?(result) result.extra.dig( :proofing_results, :context, @@ -106,7 +106,7 @@ def idv_failure(result) :state_id, :mva_exception, ).present? - is_address_exception = is_address_exception?(result) + is_address_exception = address_exception?(result) is_threatmetrix_exception = result.extra.dig( :proofing_results, :context, From 2d495828026a93152c48caa35931641fa7912fd7 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Mon, 21 Apr 2025 10:21:15 -0400 Subject: [PATCH 11/13] rate limiter fixups --- app/controllers/idv/session_errors_controller.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/controllers/idv/session_errors_controller.rb b/app/controllers/idv/session_errors_controller.rb index c3b04c213ea..a080321464d 100644 --- a/app/controllers/idv/session_errors_controller.rb +++ b/app/controllers/idv/session_errors_controller.rb @@ -8,12 +8,9 @@ class SessionErrorsController < ApplicationController before_action :confirm_two_factor_authenticated_or_user_id_in_session before_action :confirm_idv_session_step_needed - before_action :set_resolution_rate_limiter, only: [:warning, :address_warning, :failure] before_action :set_try_again_path, only: [:warning, :exception, :state_id_warning] before_action :ignore_form_step_wait_requests - attr_reader :resolution_rate_limiter - def exception log_event end @@ -64,8 +61,8 @@ def rate_limited private - def set_resolution_rate_limiter - @resolution_rate_limiter = RateLimiter.new( + def resolution_rate_limiter + @resolution_rate_limiter ||= RateLimiter.new( user: idv_session_user, rate_limit_type: :idv_resolution, ) From 619a912b9f2caec2557c32a761a79b95c4179a07 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Mon, 21 Apr 2025 15:37:36 -0400 Subject: [PATCH 12/13] redirect to address warning if there is ONLY an address exception --- .../concerns/idv/verify_info_concern.rb | 2 +- .../idv/verify_info_controller_spec.rb | 38 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb index 5fb46f681f9..bb716947409 100644 --- a/app/controllers/concerns/idv/verify_info_concern.rb +++ b/app/controllers/concerns/idv/verify_info_concern.rb @@ -93,7 +93,7 @@ def address_exception?(result) :stages, :resolution, :attributes_requiring_additional_verification, - ).include?('address') + ) == ['address'] end def idv_failure(result) diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index d24c427ef22..fb3da4d10e1 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -543,13 +543,14 @@ end end - context 'when address proofing results in an exception' do + context 'when instant verify address proofing results in an exception' do let(:document_capture_session) do DocumentCaptureSession.create(user:) end let(:success) { false } let(:errors) { {} } let(:exception) { nil } + let(:error_attributes) { nil } let(:vendor_name) { 'instantverify_placeholder' } let(:async_state) do # Here we're trying to match the store to redis -> read from redis flow this data travels @@ -569,7 +570,7 @@ errors: {}, exception: 'fake exception', vendor_name: vendor_name, - attributes_requiring_additional_verification: ['address'], + attributes_requiring_additional_verification: error_attributes, ), same_address_as_id: nil, should_proof_state_id: true, @@ -586,19 +587,32 @@ allow(controller).to receive(:load_async_state).and_return(async_state) end - it 'redirects user to address warning' do - put :show - expect(response).to redirect_to idv_session_errors_address_warning_url + context 'address is the only exception' do + let(:error_attributes) { ['address'] } + + it 'redirects user to address warning' do + put :show + expect(response).to redirect_to idv_session_errors_address_warning_url + end + + it 'logs an event' do + get :show + + expect(@analytics).to have_logged_event( + :idv_doc_auth_address_warning_visited, + step_name: 'verify_info', + remaining_submit_attempts: kind_of(Numeric), + ) + end end - it 'logs an event' do - get :show + context 'there are more instant verify exceptions' do + let(:error_attributes) { ['address', 'dob', 'ssn']} - expect(@analytics).to have_logged_event( - :idv_doc_auth_address_warning_visited, - step_name: 'verify_info', - remaining_submit_attempts: kind_of(Numeric), - ) + it 'redirects user to address warning' do + put :show + expect(response).to redirect_to idv_session_errors_exception_url + end end end From 6533ff373c32d718d0897a9020c00ac69139cb6d Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Mon, 21 Apr 2025 16:54:29 -0400 Subject: [PATCH 13/13] lint fix --- spec/controllers/idv/verify_info_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/idv/verify_info_controller_spec.rb b/spec/controllers/idv/verify_info_controller_spec.rb index fb3da4d10e1..a8433782519 100644 --- a/spec/controllers/idv/verify_info_controller_spec.rb +++ b/spec/controllers/idv/verify_info_controller_spec.rb @@ -607,7 +607,7 @@ end context 'there are more instant verify exceptions' do - let(:error_attributes) { ['address', 'dob', 'ssn']} + let(:error_attributes) { ['address', 'dob', 'ssn'] } it 'redirects user to address warning' do put :show