From 52bd33768fcf9ec20787e7e1dc4a5bdae9716415 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Fri, 2 Feb 2024 17:05:49 -0500 Subject: [PATCH 01/27] prototype of forcing doc auth with selfie on mobile --- .../idv/document_capture_controller.rb | 2 +- .../idv/hybrid_handoff_controller.rb | 2 + app/forms/idv/api_image_upload_form.rb | 7 +++ app/services/idv/session.rb | 1 + app/views/idv/hybrid_handoff/show.html.erb | 58 ++++++++++--------- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index e29ff969f11..da60b0bd515 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -59,7 +59,7 @@ def self.step_info key: :document_capture, controller: self, next_steps: [:ssn, :ipp_ssn], # :ipp_state_id - preconditions: ->(idv_session:, user:) { idv_session.flow_path == 'standard' }, + preconditions: ->(idv_session:, user:) { idv_session.flow_path == 'standard' && (!idv_session.selfie_check_required || idv.skip_hybrid_handoff) }, undo_step: ->(idv_session:, user:) do idv_session.pii_from_doc = nil idv_session.invalidate_in_person_pii_from_user! diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 61a54ec39ff..9fe0f641576 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -10,6 +10,8 @@ class HybridHandoffController < ApplicationController before_action :confirm_hybrid_handoff_needed, only: :show def show + idv_session.selfie_check_required = decorated_sp_session.selfie_required? + @selfie_check_required = idv_session.selfie_check_required analytics.idv_doc_auth_hybrid_handoff_visited(**analytics_arguments) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( diff --git a/app/forms/idv/api_image_upload_form.rb b/app/forms/idv/api_image_upload_form.rb index 37f2bd30269..6343fcbe6e2 100644 --- a/app/forms/idv/api_image_upload_form.rb +++ b/app/forms/idv/api_image_upload_form.rb @@ -249,6 +249,13 @@ def validate_images type: :not_a_file ) end + + if liveness_checking_required && !acuant_sdk_capture? + errors.add( + :selfie, t('doc_auth.errors.not_a_file'), + type: :not_a_file + ) + end end def validate_duplicate_images diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index 70bfb9a9cde..94a90c02b41 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -23,6 +23,7 @@ class Session redo_document_capture resolution_successful selfie_check_performed + selfie_check_required skip_doc_auth skip_hybrid_handoff ssn diff --git a/app/views/idv/hybrid_handoff/show.html.erb b/app/views/idv/hybrid_handoff/show.html.erb index f0ab2cb26be..2e5c4101722 100644 --- a/app/views/idv/hybrid_handoff/show.html.erb +++ b/app/views/idv/hybrid_handoff/show.html.erb @@ -54,34 +54,36 @@ -
-
-
- <%= image_tag( - asset_url('idv/laptop-icon.svg'), - alt: t('image_description.laptop'), - width: 88, - height: 88, - ) %> -
-
-

- <%= t('doc_auth.headings.upload_from_computer') %> -

- <%= t('doc_auth.info.upload_from_computer') %>  - <%= simple_form_for( - :doc_auth, - url: url_for(type: :desktop), - method: 'PUT', - class: 'margin-bottom-4', - html: { - id: 'form-to-submit-photos-through-desktop', - 'aria-label': t('forms.buttons.upload_photos'), - }, - ) do |f| %> - <%= f.submit t('forms.buttons.upload_photos'), outline: true %> - <% end %> +<% if !@selfie_check_required %> +
+
+
+ <%= image_tag( + asset_url('idv/laptop-icon.svg'), + alt: t('image_description.laptop'), + width: 88, + height: 88, + ) %> +
+
+

+ <%= t('doc_auth.headings.upload_from_computer') %> +

+ <%= t('doc_auth.info.upload_from_computer') %>  + <%= simple_form_for( + :doc_auth, + url: url_for(type: :desktop), + method: 'PUT', + class: 'margin-bottom-4', + html: { + id: 'form-to-submit-photos-through-desktop', + 'aria-label': t('forms.buttons.upload_photos'), + }, + ) do |f| %> + <%= f.submit t('forms.buttons.upload_photos'), outline: true %> + <% end %> +
-
+<% end %> <%= render 'idv/doc_auth/cancel', step: 'hybrid_handoff' %> From ddba7e80cb890478f2e9b7bf4dd794b125cd9639 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Mon, 5 Feb 2024 14:10:04 -0500 Subject: [PATCH 02/27] raise error before making true id request for liveness + cropping workflow --- .../doc_auth/lexis_nexis/requests/true_id_request.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb b/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb index bfe0213e211..adf088fede4 100644 --- a/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb +++ b/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb @@ -75,9 +75,9 @@ def workflow config.trueid_liveness_nocropping_workflow : config.trueid_noliveness_nocropping_workflow else - include_liveness? ? - config.trueid_liveness_cropping_workflow : - config.trueid_noliveness_cropping_workflow + raise 'sdsk images required' if include_liveness? + + config.trueid_noliveness_cropping_workflow end end From 22bf92035d70dad3bf4e1f030e407f9d7d6e1071 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Mon, 5 Feb 2024 14:59:31 -0500 Subject: [PATCH 03/27] comment on where to return DocAuth::ErrorRequest --- app/services/doc_auth/lexis_nexis/request.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/doc_auth/lexis_nexis/request.rb b/app/services/doc_auth/lexis_nexis/request.rb index 68ca892ec57..f31556f423c 100644 --- a/app/services/doc_auth/lexis_nexis/request.rb +++ b/app/services/doc_auth/lexis_nexis/request.rb @@ -10,6 +10,7 @@ def initialize(config:, user_uuid: nil, uuid_prefix: nil) end def fetch + # return DocAuth::Respose with DocAuth:Error if worflow invalid http_response = send_http_request return handle_invalid_response(http_response) unless http_response.success? From d503f4ebe4b1602c8c5628b60f8332f5b5da612d Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Mon, 5 Feb 2024 15:01:15 -0500 Subject: [PATCH 04/27] do not return workflow for cropping + liveness --- app/services/doc_auth/lexis_nexis/requests/true_id_request.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb b/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb index adf088fede4..62214962f95 100644 --- a/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb +++ b/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb @@ -74,9 +74,7 @@ def workflow include_liveness? ? config.trueid_liveness_nocropping_workflow : config.trueid_noliveness_nocropping_workflow - else - raise 'sdsk images required' if include_liveness? - + elsif !include_liveness? config.trueid_noliveness_cropping_workflow end end From 4cd65f989d1f445e5d8ef506623557c99a0093c4 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Tue, 13 Feb 2024 16:11:25 -0500 Subject: [PATCH 05/27] revert workflow method changes --- .../doc_auth/lexis_nexis/requests/true_id_request.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb b/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb index 62214962f95..bfe0213e211 100644 --- a/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb +++ b/app/services/doc_auth/lexis_nexis/requests/true_id_request.rb @@ -74,8 +74,10 @@ def workflow include_liveness? ? config.trueid_liveness_nocropping_workflow : config.trueid_noliveness_nocropping_workflow - elsif !include_liveness? - config.trueid_noliveness_cropping_workflow + else + include_liveness? ? + config.trueid_liveness_cropping_workflow : + config.trueid_noliveness_cropping_workflow end end From 7990e3107004dcd83476d298ec186d2b10a90155 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 16 Feb 2024 12:25:30 -0500 Subject: [PATCH 06/27] LG-12306: continue work for flow policy check for selfie. changelog: Internal, Doc Auth, Stop user to capture on desktop when selfie is required. --- .../idv/document_capture_controller.rb | 7 ++- .../idv/hybrid_handoff_controller.rb | 2 +- app/forms/idv/api_image_upload_form.rb | 7 --- app/views/idv/hybrid_handoff/show.html.erb | 5 +- .../idv/document_capture_controller_spec.rb | 43 ++++++++++++--- .../idv/hybrid_handoff_controller_spec.rb | 55 +++++++++++++++++-- 6 files changed, 94 insertions(+), 25 deletions(-) diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index da60b0bd515..79baf411dd1 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -59,7 +59,11 @@ def self.step_info key: :document_capture, controller: self, next_steps: [:ssn, :ipp_ssn], # :ipp_state_id - preconditions: ->(idv_session:, user:) { idv_session.flow_path == 'standard' && (!idv_session.selfie_check_required || idv.skip_hybrid_handoff) }, + preconditions: ->(idv_session:, user:) { + idv_session.flow_path == 'standard' && ( + !idv_session.selfie_check_required || idv_session.skip_hybrid_handoff + ) + }, undo_step: ->(idv_session:, user:) do idv_session.pii_from_doc = nil idv_session.invalidate_in_person_pii_from_user! @@ -85,6 +89,7 @@ def analytics_arguments irs_reproofing: irs_reproofing?, redo_document_capture: idv_session.redo_document_capture, skip_hybrid_handoff: idv_session.skip_hybrid_handoff, + selfie_check_required: idv_session.selfie_check_required, }.merge(ab_test_analytics_buckets) end diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 9fe0f641576..81996d310e6 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -11,7 +11,6 @@ class HybridHandoffController < ApplicationController def show idv_session.selfie_check_required = decorated_sp_session.selfie_required? - @selfie_check_required = idv_session.selfie_check_required analytics.idv_doc_auth_hybrid_handoff_visited(**analytics_arguments) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( @@ -170,6 +169,7 @@ def analytics_arguments irs_reproofing: irs_reproofing?, redo_document_capture: params[:redo] ? true : nil, skip_hybrid_handoff: idv_session.skip_hybrid_handoff, + selfie_check_required: idv_session.selfie_check_required, }.merge(ab_test_analytics_buckets) end diff --git a/app/forms/idv/api_image_upload_form.rb b/app/forms/idv/api_image_upload_form.rb index 6343fcbe6e2..37f2bd30269 100644 --- a/app/forms/idv/api_image_upload_form.rb +++ b/app/forms/idv/api_image_upload_form.rb @@ -249,13 +249,6 @@ def validate_images type: :not_a_file ) end - - if liveness_checking_required && !acuant_sdk_capture? - errors.add( - :selfie, t('doc_auth.errors.not_a_file'), - type: :not_a_file - ) - end end def validate_duplicate_images diff --git a/app/views/idv/hybrid_handoff/show.html.erb b/app/views/idv/hybrid_handoff/show.html.erb index 2e5c4101722..a577241470e 100644 --- a/app/views/idv/hybrid_handoff/show.html.erb +++ b/app/views/idv/hybrid_handoff/show.html.erb @@ -53,9 +53,7 @@ <% end %>
- -<% if !@selfie_check_required %> -
+
<%= image_tag( @@ -84,6 +82,5 @@ <% end %>
-<% end %> <%= render 'idv/doc_auth/cancel', step: 'hybrid_handoff' %> diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 6f6f8978a6d..889ccfbbaeb 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -20,12 +20,18 @@ { sample_bucket1: :sample_value1, sample_bucket2: :sample_value2 } end + # selfie related test flags + let(:doc_auth_selfie_capture_enabled) { false } + let(:sp_selfie_enabled) { false } + let(:flow_path) { 'standard' } + before do stub_sign_in(user) stub_up_to(:hybrid_handoff, idv_session: subject.idv_session) stub_analytics subject.idv_session.document_capture_session_uuid = document_capture_session_uuid - + subject.idv_session.selfie_check_required = doc_auth_selfie_capture_enabled && sp_selfie_enabled + subject.idv_session.flow_path = flow_path allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) end @@ -33,6 +39,27 @@ it 'returns a valid StepInfo object' do expect(Idv::DocumentCaptureController.step_info).to be_valid end + context 'when selfie feature is enabled system wide' do + let(:doc_auth_selfie_capture_enabled) { true } + describe 'with sp selfie disabled' do + let(:sp_selfie_enabled) { false } + it 'does not satisfy precondition' do + expect(Idv::DocumentCaptureController.step_info.preconditions.is_a?(Proc)) + expect(subject).to receive(:render). + with(:show, locals: an_instance_of(Hash)).and_call_original + get :show + expect(response).to render_template :show + end + end + describe 'with sp selfie enabled' do + let(:sp_selfie_enabled) { true } + it 'does satisfy precondition' do + expect(Idv::DocumentCaptureController.step_info.preconditions.is_a?(Proc)) + expect(subject).not_to receive(:render).with(:show, locals: an_instance_of(Hash)) + get :show + end + end + end end describe 'before_actions' do @@ -61,6 +88,7 @@ skip_hybrid_handoff: nil, irs_reproofing: false, step: 'document_capture', + selfie_check_required: sp_selfie_enabled && doc_auth_selfie_capture_enabled, }.merge(ab_test_args) end @@ -79,13 +107,11 @@ end context 'when a selfie is requested' do - before do - allow(subject).to receive(:decorated_sp_session). - and_return(double('decorated_session', { selfie_required?: true, sp_name: 'sp' })) - end + let(:doc_auth_selfie_capture_enabled) { true } + let(:sp_selfie_enabled) { true } - it 'renders the show template with selfie' do - expect(subject).to receive(:render).with( + it 'redirect back to handoff page' do + expect(subject).not_to receive(:render).with( :show, locals: hash_including( document_capture_session_uuid: document_capture_session_uuid, @@ -95,7 +121,7 @@ get :show - expect(response).to render_template :show + expect(response).to redirect_to(idv_hybrid_handoff_path) end end @@ -208,6 +234,7 @@ skip_hybrid_handoff: nil, irs_reproofing: false, step: 'document_capture', + selfie_check_required: sp_selfie_enabled && doc_auth_selfie_capture_enabled, }.merge(ab_test_args) end let(:result) { { success: true, errors: {} } } diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 8e35f7808b4..0c70a78af91 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -13,8 +13,14 @@ end let(:in_person_proofing) { false } let(:ipp_opt_in_enabled) { false } + let(:doc_auth_selfie_capture_enabled) { false } + let(:sp_selfie_enabled) { false } before do + allow(controller).to receive(:current_sp). + and_return(service_provider) + allow(controller).to receive(:sp_session). + and_return({ biometric_comparison_required: sp_selfie_enabled }) stub_sign_in(user) stub_up_to(:agreement, idv_session: subject.idv_session) stub_analytics @@ -25,6 +31,8 @@ allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled) { ipp_opt_in_enabled } + allow(IdentityConfig.store).to receive(:doc_auth_selfie_capture_enabled). + and_return(doc_auth_selfie_capture_enabled) end describe '#step_info' do @@ -58,6 +66,7 @@ redo_document_capture: nil, skip_hybrid_handoff: nil, irs_reproofing: false, + selfie_check_required: sp_selfie_enabled && doc_auth_selfie_capture_enabled, }.merge(ab_test_args) end @@ -244,6 +253,36 @@ end end end + + context 'with selfie enabled system wide' do + let(:doc_auth_selfie_capture_enabled) { true } + before do + # allow(controller).to receive(:current_sp). + # and_return(service_provider) + end + describe 'when selfie is enabled for sp' do + let(:sp_selfie_enabled) { true } + it 'pass on correct flags and states and logs correct info' do + allow(controller).to receive(:sp_session). + and_return({ biometric_comparison_required: sp_selfie_enabled }) + get :show + expect(response).to render_template :show + expect(subject.idv_session.selfie_check_required).to eq(true) + expect(@analytics).to have_logged_event(analytics_name, analytics_args) + end + end + describe 'when selfie is disabled for sp' do + let(:sp_selfie_enabled) { false } + it 'pass on correct flags and states and logs correct info' do + allow(controller).to receive(:sp_session). + and_return({ biometric_comparison_required: sp_selfie_enabled }) + get :show + expect(response).to render_template :show + expect(subject.idv_session.selfie_check_required).to eq(false) + expect(@analytics).to have_logged_event(analytics_name, analytics_args) + end + end + end end describe '#update' do @@ -260,6 +299,7 @@ analytics_id: 'Doc Auth', redo_document_capture: nil, skip_hybrid_handoff: nil, + selfie_check_required: sp_selfie_enabled && doc_auth_selfie_capture_enabled, irs_reproofing: false, telephony_response: { errors: {}, @@ -279,6 +319,12 @@ let(:document_capture_session_uuid) { '09228b6d-dd39-4925-bf82-b69104095517' } + before do + subject.idv_session.document_capture_session_uuid = document_capture_session_uuid + subject.idv_session.selfie_check_required = + sp_selfie_enabled && doc_auth_selfie_capture_enabled + end + it 'invalidates future steps' do expect(subject).to receive(:clear_future_steps!) @@ -292,10 +338,6 @@ expect(@analytics).to have_logged_event(analytics_name, analytics_args) end - before do - subject.idv_session.document_capture_session_uuid = document_capture_session_uuid - end - it 'sends a doc auth link' do expect(Telephony).to receive(:send_doc_auth_link).with( hash_including( @@ -319,6 +361,7 @@ redo_document_capture: nil, skip_hybrid_handoff: nil, irs_reproofing: false, + selfie_check_required: doc_auth_selfie_capture_enabled && sp_selfie_enabled, }.merge(ab_test_args) end @@ -327,6 +370,10 @@ type: 'desktop', } end + before do + subject.idv_session.selfie_check_required = + sp_selfie_enabled && doc_auth_selfie_capture_enabled + end it 'sends analytics_submitted event for desktop' do put :update, params: params From dd2f77c17ef7c5e4afc4a64daac6fcb3efb087a5 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 20 Feb 2024 09:49:33 -0500 Subject: [PATCH 07/27] LG-12306: cleanup change. --- app/views/idv/hybrid_handoff/show.html.erb | 57 +++++++++++----------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/app/views/idv/hybrid_handoff/show.html.erb b/app/views/idv/hybrid_handoff/show.html.erb index a577241470e..f0ab2cb26be 100644 --- a/app/views/idv/hybrid_handoff/show.html.erb +++ b/app/views/idv/hybrid_handoff/show.html.erb @@ -53,34 +53,35 @@ <% end %> -
-
-
- <%= image_tag( - asset_url('idv/laptop-icon.svg'), - alt: t('image_description.laptop'), - width: 88, - height: 88, - ) %> -
-
-

- <%= t('doc_auth.headings.upload_from_computer') %> -

- <%= t('doc_auth.info.upload_from_computer') %>  - <%= simple_form_for( - :doc_auth, - url: url_for(type: :desktop), - method: 'PUT', - class: 'margin-bottom-4', - html: { - id: 'form-to-submit-photos-through-desktop', - 'aria-label': t('forms.buttons.upload_photos'), - }, - ) do |f| %> - <%= f.submit t('forms.buttons.upload_photos'), outline: true %> - <% end %> -
+ +
+
+
+ <%= image_tag( + asset_url('idv/laptop-icon.svg'), + alt: t('image_description.laptop'), + width: 88, + height: 88, + ) %> +
+
+

+ <%= t('doc_auth.headings.upload_from_computer') %> +

+ <%= t('doc_auth.info.upload_from_computer') %>  + <%= simple_form_for( + :doc_auth, + url: url_for(type: :desktop), + method: 'PUT', + class: 'margin-bottom-4', + html: { + id: 'form-to-submit-photos-through-desktop', + 'aria-label': t('forms.buttons.upload_photos'), + }, + ) do |f| %> + <%= f.submit t('forms.buttons.upload_photos'), outline: true %> + <% end %>
+
<%= render 'idv/doc_auth/cancel', step: 'hybrid_handoff' %> From b8f19315e436e8d1becd96c038ddefd9c61700d8 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 20 Feb 2024 09:58:45 -0500 Subject: [PATCH 08/27] LG-12306: test validation. --- spec/controllers/idv/document_capture_controller_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 889ccfbbaeb..61b23b35385 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -57,6 +57,7 @@ expect(Idv::DocumentCaptureController.step_info.preconditions.is_a?(Proc)) expect(subject).not_to receive(:render).with(:show, locals: an_instance_of(Hash)) get :show + expect(response).to redirect_to(idv_hybrid_handoff_path) end end end From 79022fa2f10a812f4cca7f5594845c92536f97b6 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 08:35:04 -0500 Subject: [PATCH 09/27] LG-12306: analytics event test. Mark idv_session selfie_check_reqequired in agreement controller. --- app/controllers/idv/agreement_controller.rb | 2 +- .../idv/hybrid_handoff_controller.rb | 1 - .../document_capture_controller.rb | 1 + .../idv/hybrid_handoff_controller_spec.rb | 2 + .../document_capture_controller_spec.rb | 2 + spec/features/idv/analytics_spec.rb | 87 ++++++++++--------- 6 files changed, 50 insertions(+), 45 deletions(-) diff --git a/app/controllers/idv/agreement_controller.rb b/app/controllers/idv/agreement_controller.rb index 8d96f299562..efe507c08ac 100644 --- a/app/controllers/idv/agreement_controller.rb +++ b/app/controllers/idv/agreement_controller.rb @@ -8,8 +8,8 @@ class AgreementController < ApplicationController before_action :confirm_step_allowed def show + idv_session.selfie_check_required = decorated_sp_session.selfie_required? analytics.idv_doc_auth_agreement_visited(**analytics_arguments) - Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( 'agreement', :view, true diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 81996d310e6..f75a238464f 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -10,7 +10,6 @@ class HybridHandoffController < ApplicationController before_action :confirm_hybrid_handoff_needed, only: :show def show - idv_session.selfie_check_required = decorated_sp_session.selfie_required? analytics.idv_doc_auth_hybrid_handoff_visited(**analytics_arguments) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( diff --git a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb index b554e376e0e..6e88a0da847 100644 --- a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb +++ b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb @@ -55,6 +55,7 @@ def analytics_arguments step: 'document_capture', analytics_id: 'Doc Auth', irs_reproofing: irs_reproofing?, + selfie_check_required: decorated_sp_session.selfie_required?, }.merge( ab_test_analytics_buckets, ) diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 0c70a78af91..6977aedf677 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -23,6 +23,8 @@ and_return({ biometric_comparison_required: sp_selfie_enabled }) stub_sign_in(user) stub_up_to(:agreement, idv_session: subject.idv_session) + # precondition set in agreement controller + subject.idv_session.selfie_check_required = sp_selfie_enabled && doc_auth_selfie_capture_enabled stub_analytics stub_attempts_tracker allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) diff --git a/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb index f1774b9e0f3..aae38b16823 100644 --- a/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb +++ b/spec/controllers/idv/hybrid_mobile/document_capture_controller_spec.rb @@ -58,6 +58,7 @@ flow_path: 'hybrid', irs_reproofing: false, step: 'document_capture', + selfie_check_required: boolean, }.merge(ab_test_args) end @@ -181,6 +182,7 @@ flow_path: 'hybrid', irs_reproofing: false, step: 'document_capture', + selfie_check_required: boolean, }.merge(ab_test_args) end diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index ffa7aa1cf13..486597d10d5 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -54,13 +54,13 @@ success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth hybrid handoff visited' => { - step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth hybrid handoff submitted' => { - success: true, errors: {}, destination: :document_capture, flow_path: 'standard', step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + success: true, errors: {}, destination: :document_capture, flow_path: 'standard', step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth document_capture visited' => { - flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'Frontend: IdV: front image added' => { width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: anything, acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil @@ -75,7 +75,7 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {} }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth ssn visited' => { flow_path: 'standard', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false @@ -162,13 +162,13 @@ success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth hybrid handoff visited' => { - step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth hybrid handoff submitted' => { - success: true, errors: hash_including(message: nil), destination: :link_sent, flow_path: 'hybrid', step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, telephony_response: hash_including(errors: {}, message_id: 'fake-message-id', request_id: 'fake-message-request-id', success: true) + success: true, errors: hash_including(message: nil), destination: :link_sent, flow_path: 'hybrid', step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, telephony_response: hash_including(errors: {}, message_id: 'fake-message-id', request_id: 'fake-message-request-id', success: true), selfie_check_required: boolean }, 'IdV: doc auth document_capture visited' => { - flow_path: 'hybrid', step: 'document_capture', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false + flow_path: 'hybrid', step: 'document_capture', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, 'Frontend: IdV: front image added' => { width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'hybrid', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: anything, acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil @@ -183,7 +183,7 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'hybrid', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {} }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'hybrid', step: 'document_capture', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false + success: true, errors: {}, flow_path: 'hybrid', step: 'document_capture', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth ssn visited' => { flow_path: 'hybrid', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false @@ -267,13 +267,13 @@ success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth hybrid handoff visited' => { - step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth hybrid handoff submitted' => { - success: true, errors: {}, destination: :document_capture, flow_path: 'standard', redo_document_capture: nil, step: 'hybrid_handoff', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + success: true, errors: {}, destination: :document_capture, flow_path: 'standard', redo_document_capture: nil, step: 'hybrid_handoff', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth document_capture visited' => { - flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false + flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, 'Frontend: IdV: front image added' => { width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: anything, acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil @@ -288,7 +288,7 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: nil, liveness_checking_required: boolean, classification_info: {} }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false + success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth ssn visited' => { flow_path: 'standard', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false @@ -354,13 +354,13 @@ success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth hybrid handoff visited' => { - step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth hybrid handoff submitted' => { - success: true, errors: {}, destination: :document_capture, flow_path: 'standard', redo_document_capture: nil, step: 'hybrid_handoff', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + success: true, errors: {}, destination: :document_capture, flow_path: 'standard', redo_document_capture: nil, step: 'hybrid_handoff', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'IdV: doc auth document_capture visited' => { - flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean }, 'Frontend: IdV: front image added' => { width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: anything, acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil @@ -468,32 +468,26 @@ } end - let(:happy_selfie_path_events) do + let(:happy_mobile_selfie_path_events) do { 'IdV: intro visited' => {}, 'IdV: doc auth welcome visited' => { - step: 'welcome', analytics_id: 'Doc Auth', irs_reproofing: false, skip_hybrid_handoff: nil, lexisnexis_instant_verify_workflow_ab_test_bucket: :default + step: 'welcome', analytics_id: 'Doc Auth', irs_reproofing: false, skip_hybrid_handoff: anything, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth welcome submitted' => { - step: 'welcome', analytics_id: 'Doc Auth', irs_reproofing: false, skip_hybrid_handoff: nil, lexisnexis_instant_verify_workflow_ab_test_bucket: :default + step: 'welcome', analytics_id: 'Doc Auth', irs_reproofing: false, skip_hybrid_handoff: anything, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth agreement visited' => { - step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default + step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: anything, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: consent checkbox toggled' => { checked: true, }, 'IdV: doc auth agreement submitted' => { - success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default - }, - 'IdV: doc auth hybrid handoff visited' => { - step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false - }, - 'IdV: doc auth hybrid handoff submitted' => { - success: true, errors: {}, destination: :document_capture, flow_path: 'standard', step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false + success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: anything, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, 'IdV: doc auth document_capture visited' => { - flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, skip_hybrid_handoff: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false + flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, skip_hybrid_handoff: anything, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, 'Frontend: IdV: front image added' => { width: 284, height: 38, mimeType: 'image/png', source: 'upload', size: 3694, captureAttempts: 1, flow_path: 'standard', acuant_sdk_upgrade_a_b_testing_enabled: 'false', use_alternate_sdk: anything, acuant_version: anything, acuantCaptureMode: nil, fingerprint: anything, failedImageResubmission: boolean, documentType: nil, dpi: nil, glare: nil, glareScoreThreshold: nil, isAssessedAsBlurry: nil, isAssessedAsGlare: nil, isAssessedAsUnsupported: nil, moire: nil, sharpness: nil, sharpnessScoreThreshold: nil, assessment: nil @@ -508,33 +502,33 @@ success: true, errors: {}, user_id: user.uuid, submit_attempts: 1, remaining_submit_attempts: 3, flow_path: 'standard', attention_with_barcode: false, front_image_fingerprint: an_instance_of(String), back_image_fingerprint: an_instance_of(String), selfie_image_fingerprint: an_instance_of(String), liveness_checking_required: boolean, classification_info: {} }, 'IdV: doc auth document_capture submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, skip_hybrid_handoff: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false + success: true, errors: {}, flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, skip_hybrid_handoff: anything, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, :idv_selfie_image_file_uploaded => { captureAttempts: 1, failedImageResubmission: nil, fingerprint: 'aIzxkX_iMtoxFOURZr55qkshs53emQKUOr7VfTf6G1Q', flow_path: 'standard', height: 38, mimeType: 'image/png', size: 3694, source: 'upload', width: 284 }, 'IdV: doc auth ssn visited' => { - flow_path: 'standard', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false + flow_path: 'standard', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, analytics_id: 'Doc Auth', irs_reproofing: false }, 'IdV: doc auth ssn submitted' => { - success: true, errors: {}, flow_path: 'standard', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false + success: true, errors: {}, flow_path: 'standard', step: 'ssn', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, analytics_id: 'Doc Auth', irs_reproofing: false }, 'IdV: doc auth verify visited' => { - flow_path: 'standard', step: 'verify', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false + flow_path: 'standard', step: 'verify', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, analytics_id: 'Doc Auth', irs_reproofing: false }, 'IdV: doc auth verify submitted' => { - flow_path: 'standard', step: 'verify', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, analytics_id: 'Doc Auth', irs_reproofing: false + flow_path: 'standard', step: 'verify', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, analytics_id: 'Doc Auth', irs_reproofing: false }, 'IdV: doc auth verify proofing results' => { - success: true, errors: {}, flow_path: 'standard', address_edited: false, address_line2_present: false, analytics_id: 'Doc Auth', ssn_is_unique: true, step: 'verify', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, irs_reproofing: false, skip_hybrid_handoff: nil, + success: true, errors: {}, flow_path: 'standard', address_edited: false, address_line2_present: false, analytics_id: 'Doc Auth', ssn_is_unique: true, step: 'verify', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, irs_reproofing: false, skip_hybrid_handoff: anything, proofing_results: { exception: nil, timed_out: false, threatmetrix_review_status: 'pass', context: { device_profiling_adjudication_reason: 'device_profiling_result_pass', resolution_adjudication_reason: 'pass_resolution_and_state_id', should_proof_state_id: true, stages: { resolution: { success: true, errors: {}, exception: nil, timed_out: false, transaction_id: 'resolution-mock-transaction-id-123', reference: 'aaa-bbb-ccc', can_pass_with_additional_verification: false, attributes_requiring_additional_verification: [], vendor_name: 'ResolutionMock', vendor_workflow: nil }, residential_address: { attributes_requiring_additional_verification: [], can_pass_with_additional_verification: false, errors: {}, exception: nil, reference: '', success: true, timed_out: false, transaction_id: '', vendor_name: 'ResidentialAddressNotRequired', vendor_workflow: nil }, state_id: { success: true, errors: {}, exception: nil, mva_exception: nil, timed_out: false, transaction_id: 'state-id-mock-transaction-id-456', vendor_name: 'StateIdMock', verified_attributes: [], state: 'MT', state_id_jurisdiction: 'ND', state_id_number: '#############' }, threatmetrix: threatmetrix_response } } } }, 'IdV: phone of record visited' => { - acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, + acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, 'IdV: phone confirmation form' => { - success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, otp_delivery_preference: 'sms', + success: true, errors: {}, phone_type: :mobile, types: [:fixed_or_mobile], carrier: 'Test Mobile Carrier', country_code: 'US', area_code: '202', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, otp_delivery_preference: 'sms', proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass' } }, 'IdV: phone confirmation vendor' => { @@ -549,19 +543,19 @@ proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' }, }, 'IdV: phone confirmation otp submitted' => { - success: true, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, code_expired: false, code_matches: true, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, + success: true, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, code_expired: false, code_matches: true, second_factor_attempts_count: 0, second_factor_locked_at: nil, errors: {}, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, :idv_enter_password_visited => { - address_verification_method: 'phone', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, + address_verification_method: 'phone', acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, :idv_enter_password_submitted => { - success: true, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, + success: true, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: final resolution' => { - success: true, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: nil, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, + success: true, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, skip_hybrid_handoff: anything, fraud_review_pending: false, fraud_rejection: false, gpo_verification_pending: false, in_person_verification_pending: false, deactivation_reason: nil, proofing_components: { document_check: 'mock', document_type: 'state_id', source_check: 'aamva', resolution_check: 'lexis_nexis', threatmetrix: threatmetrix, threatmetrix_review_status: 'pass', address_check: 'lexis_nexis_address' } }, 'IdV: personal key visited' => { @@ -850,7 +844,7 @@ def wait_for_event(event, wait) and_return(true) allow_any_instance_of(FederatedProtocols::Oidc). to receive(:biometric_comparison_required?). - and_return({ biometric_comparison_required: true }) + and_return(true) allow_any_instance_of(DocAuth::Response).to receive(:selfie_status).and_return(:success) allow_any_instance_of(DocumentCaptureSessionResult). @@ -858,10 +852,17 @@ def wait_for_event(event, wait) mobile_device = Browser.new(mobile_user_agent) allow(BrowserCache).to receive(:parse).and_return(mobile_device) + allow(BrowserCache).to receive(:get).and_return(mobile_device) + + # mock mobile device as cameraCapable + allow_any_instance_of(ActionController::Parameters).to + receive(:[]).and_wrap_original do |impl, param_name| + param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) + end perform_in_browser(:mobile) do sign_in_and_2fa_user(user) - visit_idp_from_sp_with_ial2(:oidc) + visit_idp_from_sp_with_ial2(:oidc, biometric_comparison_required: true) complete_doc_auth_steps_before_document_capture_step attach_images @@ -880,7 +881,7 @@ def wait_for_event(event, wait) end it 'records all of the events' do - happy_selfie_path_events.each do |event, attributes| + happy_mobile_selfie_path_events.each do |event, attributes| expect(fake_analytics).to have_logged_event(event, attributes) end end @@ -901,7 +902,7 @@ def wait_for_event(event, wait) it 'records all of the events' do aggregate_failures 'analytics events' do - happy_selfie_path_events.each do |event, attributes| + happy_mobile_selfie_path_events.each do |event, attributes| expect(fake_analytics).to have_logged_event(event, attributes) end end From fa1d44836cddb21d20beeed7ab7c8f6b352b314c Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 08:41:59 -0500 Subject: [PATCH 10/27] LG-12306: test clean up. --- spec/controllers/idv/hybrid_handoff_controller_spec.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 6977aedf677..0e4d46842ad 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -19,8 +19,6 @@ before do allow(controller).to receive(:current_sp). and_return(service_provider) - allow(controller).to receive(:sp_session). - and_return({ biometric_comparison_required: sp_selfie_enabled }) stub_sign_in(user) stub_up_to(:agreement, idv_session: subject.idv_session) # precondition set in agreement controller @@ -265,8 +263,6 @@ describe 'when selfie is enabled for sp' do let(:sp_selfie_enabled) { true } it 'pass on correct flags and states and logs correct info' do - allow(controller).to receive(:sp_session). - and_return({ biometric_comparison_required: sp_selfie_enabled }) get :show expect(response).to render_template :show expect(subject.idv_session.selfie_check_required).to eq(true) @@ -276,8 +272,6 @@ describe 'when selfie is disabled for sp' do let(:sp_selfie_enabled) { false } it 'pass on correct flags and states and logs correct info' do - allow(controller).to receive(:sp_session). - and_return({ biometric_comparison_required: sp_selfie_enabled }) get :show expect(response).to render_template :show expect(subject.idv_session.selfie_check_required).to eq(false) From 0c2e4327cbab0d957a5f01a8c0d781e117c0bdad Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 09:07:09 -0500 Subject: [PATCH 11/27] LG-12306: feature test. --- app/services/doc_auth/lexis_nexis/request.rb | 2 +- .../idv/doc_auth/hybrid_handoff_spec.rb | 30 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/app/services/doc_auth/lexis_nexis/request.rb b/app/services/doc_auth/lexis_nexis/request.rb index f31556f423c..a15b1669e9d 100644 --- a/app/services/doc_auth/lexis_nexis/request.rb +++ b/app/services/doc_auth/lexis_nexis/request.rb @@ -10,7 +10,7 @@ def initialize(config:, user_uuid: nil, uuid_prefix: nil) end def fetch - # return DocAuth::Respose with DocAuth:Error if worflow invalid + # return DocAuth::Respose with DocAuth:Error if workflow invalid http_response = send_http_request return handle_invalid_response(http_response) unless http_response.success? diff --git a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb index 53f1bbd5248..3c99f3f304e 100644 --- a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb +++ b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb @@ -12,15 +12,12 @@ IdentityConfig.store.idv_send_link_attempt_window_in_minutes end - before do - sign_in_and_2fa_user - allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) - allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker). - and_return(fake_attempts_tracker) - end - context 'on a desktop device send link' do before do + sign_in_and_2fa_user + allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) + allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker). + and_return(fake_attempts_tracker) complete_doc_auth_steps_before_hybrid_handoff_step end @@ -209,4 +206,23 @@ expect(document_capture_session).to have_attributes(requested_at: a_kind_of(Time)) end end + + context 'on a desktop device when selfie required', js: true do + let(:user) { user_with_2fa } + before do + expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once). + and_return(true) + sign_in_and_2fa_user(user) + visit_idp_from_sp_with_ial2(:oidc, biometric_comparison_required: true) + allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) + allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker). + and_return(fake_attempts_tracker) + complete_doc_auth_steps_before_document_capture_step + end + it 'it prevents from proceeding to document capture' do + expect(page).to have_current_path(idv_hybrid_handoff_path) + click_on t('forms.buttons.upload_photos') + expect(page).to have_current_path(idv_hybrid_handoff_path) + end + end end From 2723c738fb74284047d22df88fd804ed957b5b6e Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 11:15:44 -0500 Subject: [PATCH 12/27] LG-12306: format change cause failure. --- spec/features/idv/analytics_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index 486597d10d5..2f5dc6626ff 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -855,8 +855,8 @@ def wait_for_event(event, wait) allow(BrowserCache).to receive(:get).and_return(mobile_device) # mock mobile device as cameraCapable - allow_any_instance_of(ActionController::Parameters).to - receive(:[]).and_wrap_original do |impl, param_name| + allow_any_instance_of(ActionController::Parameters). + to receive(:[]).and_wrap_original do |impl, param_name| param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) end From e4b9192ff47df899d4dfe79a980dcd665ff3a359 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 15:17:19 -0500 Subject: [PATCH 13/27] LG-12306: dealing with dynamics of selfie requirement. --- app/controllers/concerns/idv_step_concern.rb | 2 ++ app/controllers/idv/agreement_controller.rb | 5 +++-- .../idv/document_capture_controller.rb | 4 +++- .../idv/document_capture_controller_spec.rb | 3 ++- .../idv/hybrid_handoff_controller_spec.rb | 16 +++------------- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/app/controllers/concerns/idv_step_concern.rb b/app/controllers/concerns/idv_step_concern.rb index 9a17add16e9..4eb382645aa 100644 --- a/app/controllers/concerns/idv_step_concern.rb +++ b/app/controllers/concerns/idv_step_concern.rb @@ -108,6 +108,8 @@ def flow_policy end def confirm_step_allowed + # set it everytime, since user may switch SP + idv_session.selfie_check_required = decorated_sp_session.selfie_required? return if flow_policy.controller_allowed?(controller: self.class) redirect_to url_for_latest_step diff --git a/app/controllers/idv/agreement_controller.rb b/app/controllers/idv/agreement_controller.rb index efe507c08ac..ec60d440fce 100644 --- a/app/controllers/idv/agreement_controller.rb +++ b/app/controllers/idv/agreement_controller.rb @@ -8,7 +8,6 @@ class AgreementController < ApplicationController before_action :confirm_step_allowed def show - idv_session.selfie_check_required = decorated_sp_session.selfie_required? analytics.idv_doc_auth_agreement_visited(**analytics_arguments) Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( 'agreement', :view, @@ -49,7 +48,9 @@ def self.step_info key: :agreement, controller: self, next_steps: [:hybrid_handoff, :document_capture, :how_to_verify], - preconditions: ->(idv_session:, user:) { idv_session.welcome_visited }, + preconditions: ->(idv_session:, user:) { + idv_session.welcome_visited + }, undo_step: ->(idv_session:, user:) do idv_session.idv_consent_given = nil idv_session.skip_hybrid_handoff = nil diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 79baf411dd1..ca439f51379 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -61,7 +61,9 @@ def self.step_info next_steps: [:ssn, :ipp_ssn], # :ipp_state_id preconditions: ->(idv_session:, user:) { idv_session.flow_path == 'standard' && ( - !idv_session.selfie_check_required || idv_session.skip_hybrid_handoff + # mobile + idv_session.skip_hybrid_handoff || + !idv_session.selfie_check_required # desktop but selfie not required ) }, undo_step: ->(idv_session:, user:) do diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 61b23b35385..3a810533826 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -30,7 +30,8 @@ stub_up_to(:hybrid_handoff, idv_session: subject.idv_session) stub_analytics subject.idv_session.document_capture_session_uuid = document_capture_session_uuid - subject.idv_session.selfie_check_required = doc_auth_selfie_capture_enabled && sp_selfie_enabled + allow(controller.decorated_sp_session).to receive(:selfie_required?). + and_return(doc_auth_selfie_capture_enabled && sp_selfie_enabled) subject.idv_session.flow_path = flow_path allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) end diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 0e4d46842ad..4271313d830 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -21,12 +21,12 @@ and_return(service_provider) stub_sign_in(user) stub_up_to(:agreement, idv_session: subject.idv_session) - # precondition set in agreement controller - subject.idv_session.selfie_check_required = sp_selfie_enabled && doc_auth_selfie_capture_enabled stub_analytics stub_attempts_tracker allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) allow(subject.idv_session).to receive(:service_provider).and_return(service_provider) + allow(subject.decorated_sp_session).to receive(:selfie_required?). + and_return(sp_selfie_enabled && doc_auth_selfie_capture_enabled) allow(IdentityConfig.store).to receive(:in_person_proofing_enabled) { in_person_proofing } allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled) { ipp_opt_in_enabled @@ -256,17 +256,13 @@ context 'with selfie enabled system wide' do let(:doc_auth_selfie_capture_enabled) { true } - before do - # allow(controller).to receive(:current_sp). - # and_return(service_provider) - end describe 'when selfie is enabled for sp' do let(:sp_selfie_enabled) { true } it 'pass on correct flags and states and logs correct info' do get :show expect(response).to render_template :show - expect(subject.idv_session.selfie_check_required).to eq(true) expect(@analytics).to have_logged_event(analytics_name, analytics_args) + expect(subject.idv_session.selfie_check_required).to eq(true) end end describe 'when selfie is disabled for sp' do @@ -317,8 +313,6 @@ before do subject.idv_session.document_capture_session_uuid = document_capture_session_uuid - subject.idv_session.selfie_check_required = - sp_selfie_enabled && doc_auth_selfie_capture_enabled end it 'invalidates future steps' do @@ -366,10 +360,6 @@ type: 'desktop', } end - before do - subject.idv_session.selfie_check_required = - sp_selfie_enabled && doc_auth_selfie_capture_enabled - end it 'sends analytics_submitted event for desktop' do put :update, params: params From 4ae1ddf5434131e9a2ec5e170d2cfddc485c9144 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 16:54:10 -0500 Subject: [PATCH 14/27] LG-12306: fix test. --- .../idv/doc_auth/document_capture_spec.rb | 347 ++++++++++-------- 1 file changed, 185 insertions(+), 162 deletions(-) diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index db820bf5297..ded4cbea9a9 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -240,165 +240,59 @@ before do allow_any_instance_of(FederatedProtocols::Oidc). to receive(:biometric_comparison_required?). - and_return({ biometric_comparison_required: true }) + and_return(true) end - it 'proceeds to the next page with valid info, including a selfie image' do - perform_in_browser(:mobile) do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - - expect(page).to have_current_path(idv_document_capture_url) - expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) - expect_doc_capture_page_header(t('doc_auth.headings.document_capture_with_selfie')) - expect_doc_capture_id_subheader - expect_doc_capture_selfie_subheader - attach_liveness_images - submit_images - - expect(page).to have_current_path(idv_ssn_url) - expect_costing_for_document - expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') - - expect(page).to have_current_path(idv_ssn_url) - fill_out_ssn_form_ok - click_idv_continue - complete_verify_step - expect(page).to have_current_path(idv_phone_url) + context 'on mobile platform' do + before do + # mock mobile device as cameraCapable, this allows us to proce + allow_any_instance_of(ActionController::Parameters). + to receive(:[]).and_wrap_original do |impl, param_name| + param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) + end end - end - context 'selfie with no liveness or poor quality is uploaded', allow_browser_log: true do - it 'try again and page show no liveness inline error message' do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_no_liveness.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_no_liveness.yml' - ), - ) - submit_images - message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) - expect(page).to have_content(message) - detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live')) - security_message = strip_tags( - t( - 'idv.warning.attempts_html', - count: IdentityConfig.store.doc_auth_max_attempts - 1, - ), - ) - expect(page).to have_content(detail_message << "\n" << security_message) - review_issues_header = strip_tags( - t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), - ) - expect(page).to have_content(review_issues_header) - expect(page).to have_current_path(idv_document_capture_path) - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(inline_error) - end + it 'proceeds to the next page with valid info, including a selfie image' do + perform_in_browser(:mobile) do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step - it 'try again and page show poor quality inline error message' do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_poor_quality.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_poor_quality.yml' - ), - ) - submit_images - message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) - expect(page).to have_content(message) - detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) - security_message = strip_tags( - t( - 'idv.warning.attempts_html', - count: IdentityConfig.store.doc_auth_max_attempts - 1, - ), - ) - expect(page).to have_content(detail_message << "\n" << security_message) - review_issues_header = strip_tags( - t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), - ) - expect(page).to have_content(review_issues_header) - expect(page).to have_current_path(idv_document_capture_path) - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(inline_error) - end + expect(page).to have_current_path(idv_document_capture_url) + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + expect_doc_capture_page_header(t('doc_auth.headings.document_capture_with_selfie')) + expect_doc_capture_id_subheader + expect_doc_capture_selfie_subheader + attach_liveness_images + submit_images - it 'try again and page show selfie fail inline error message' do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_portrait_match_failure.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_portrait_match_failure.yml' - ), - ) - submit_images - message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) - expect(page).to have_content(message) - detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) - security_message = strip_tags( - t( - 'idv.warning.attempts_html', - count: IdentityConfig.store.doc_auth_max_attempts - 1, - ), - ) - expect(page).to have_content(detail_message << "\n" << security_message) - review_issues_header = strip_tags( - t('errors.doc_auth.selfie_fail_heading'), - ) - expect(page).to have_content(review_issues_header) - expect(page).to have_current_path(idv_document_capture_path) - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(inline_error) + expect(page).to have_current_path(idv_ssn_url) + expect_costing_for_document + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + + expect(page).to have_current_path(idv_ssn_url) + fill_out_ssn_form_ok + click_idv_continue + complete_verify_step + expect(page).to have_current_path(idv_phone_url) + end end - context 'with Attention with Barcode' do - it 'try again and page show selfie fail inline error message' do + context 'selfie with no liveness or poor quality is uploaded', allow_browser_log: true do + it 'try again and page show no liveness inline error message' do visit_idp_from_oidc_sp_with_ial2 sign_in_and_2fa_user(user) complete_doc_auth_steps_before_document_capture_step attach_images( Rails.root.join( 'spec', 'fixtures', - 'ial2_test_credential_barcode_attention_liveness_fail.yml' + 'ial2_test_credential_no_liveness.yml' ), ) attach_selfie( Rails.root.join( 'spec', 'fixtures', - 'ial2_test_credential_barcode_attention_liveness_fail.yml' + 'ial2_test_credential_no_liveness.yml' ), ) submit_images @@ -411,7 +305,6 @@ count: IdentityConfig.store.doc_auth_max_attempts - 1, ), ) - expect(page).to have_content(detail_message << "\n" << security_message) review_issues_header = strip_tags( t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), @@ -423,39 +316,169 @@ inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) expect(page).to have_content(inline_error) end - end - end - context 'when selfie check is not enabled (flag off, and/or in production)' do - let(:selfie_check_enabled) { false } - it 'proceeds to the next page with valid info, excluding a selfie image' do - perform_in_browser(:mobile) do + it 'try again and page show poor quality inline error message' do visit_idp_from_oidc_sp_with_ial2 sign_in_and_2fa_user(user) complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_poor_quality.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_poor_quality.yml' + ), + ) + submit_images + message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end - expect(page).to have_current_path(idv_document_capture_url) - expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - - expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) - - expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - attach_images + it 'try again and page show selfie fail inline error message' do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_portrait_match_failure.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_portrait_match_failure.yml' + ), + ) submit_images + message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_fail_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end - expect(page).to have_current_path(idv_ssn_url) - expect_costing_for_document - expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + context 'with Attention with Barcode' do + it 'try again and page show selfie fail inline error message' do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_barcode_attention_liveness_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_barcode_attention_liveness_fail.yml' + ), + ) + submit_images + message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end + end + end - expect(page).to have_current_path(idv_ssn_url) - fill_out_ssn_form_ok - click_idv_continue - complete_verify_step - expect(page).to have_current_path(idv_phone_url) + context 'when selfie check is not enabled (flag off, and/or in production)' do + let(:selfie_check_enabled) { false } + it 'proceeds to the next page with valid info, excluding a selfie image' do + perform_in_browser(:mobile) do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + + expect(page).to have_current_path(idv_document_capture_url) + expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) + + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + + expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) + attach_images + submit_images + + expect(page).to have_current_path(idv_ssn_url) + expect_costing_for_document + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + + expect(page).to have_current_path(idv_ssn_url) + fill_out_ssn_form_ok + click_idv_continue + complete_verify_step + expect(page).to have_current_path(idv_phone_url) + end end end end end + context 'on desktop' do + it 'cannot proceed to document capture page' do + perform_in_browser(:desktop) do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_hybrid_handoff_step + # we still have option to continue + expect(page).to have_current_path(idv_hybrid_handoff_path) + click_on t('forms.buttons.upload_photos') + expect(page).to have_current_path(idv_hybrid_handoff_path) + end + end + end end def expect_costing_for_document From 07618f5a3a1bf7b1280b5dfe5dcca277931990ed Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Wed, 21 Feb 2024 20:05:55 -0500 Subject: [PATCH 15/27] LG-12306: fix test. --- spec/features/idv/doc_auth/redo_document_capture_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/features/idv/doc_auth/redo_document_capture_spec.rb b/spec/features/idv/doc_auth/redo_document_capture_spec.rb index 0916b126bbd..3b756633c77 100644 --- a/spec/features/idv/doc_auth/redo_document_capture_spec.rb +++ b/spec/features/idv/doc_auth/redo_document_capture_spec.rb @@ -276,6 +276,13 @@ end context 'when selfie is enabled' do + before do + # mock mobile device as cameraCapable + allow_any_instance_of(ActionController::Parameters). + to receive(:[]).and_wrap_original do |impl, param_name| + param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) + end + end context 'error due to data issue with 2xx status code', allow_browser_log: true do before do expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once). From 8a5127b8aeaa0a83a89d0374f2c0e0661442ca1e Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 22 Feb 2024 08:50:29 -0500 Subject: [PATCH 16/27] LG-12306: fix test. --- spec/features/idv/step_up_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/features/idv/step_up_spec.rb b/spec/features/idv/step_up_spec.rb index 3085068aa52..77de66e3b28 100644 --- a/spec/features/idv/step_up_spec.rb +++ b/spec/features/idv/step_up_spec.rb @@ -13,6 +13,11 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_selfie_capture_enabled).and_return(true) + # mock mobile device as cameraCapable + allow_any_instance_of(ActionController::Parameters). + to receive(:[]).and_wrap_original do |impl, param_name| + param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) + end end scenario 'User with active profile can redo idv when selfie required', js: true do From ea59e2f899bc16c172d6bf2a04209644a814e08d Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Thu, 22 Feb 2024 12:28:54 -0500 Subject: [PATCH 17/27] LG-12306: format. --- app/controllers/idv/agreement_controller.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/controllers/idv/agreement_controller.rb b/app/controllers/idv/agreement_controller.rb index ec60d440fce..7cb7df4591d 100644 --- a/app/controllers/idv/agreement_controller.rb +++ b/app/controllers/idv/agreement_controller.rb @@ -48,9 +48,7 @@ def self.step_info key: :agreement, controller: self, next_steps: [:hybrid_handoff, :document_capture, :how_to_verify], - preconditions: ->(idv_session:, user:) { - idv_session.welcome_visited - }, + preconditions: ->(idv_session:, user:) { idv_session.welcome_visited }, undo_step: ->(idv_session:, user:) do idv_session.idv_consent_given = nil idv_session.skip_hybrid_handoff = nil From 580cea7d379af011b478ace3ac4c47d45226e50b Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 23 Feb 2024 10:08:55 -0500 Subject: [PATCH 18/27] LG-12306: add configuration flag to indicate selfie allowed on desktop, mainly for testing purpose. --- app/controllers/idv/agreement_controller.rb | 1 + .../idv/document_capture_controller.rb | 3 +- .../idv/hybrid_handoff_controller.rb | 3 +- app/services/idv/session.rb | 4 ++ config/application.yml.default | 3 + lib/identity_config.rb | 1 + .../idv/document_capture_controller_spec.rb | 43 ++++++++++---- spec/features/idv/analytics_spec.rb | 9 +-- .../idv/doc_auth/document_capture_spec.rb | 59 +++++++++++++++---- .../idv/doc_auth/hybrid_handoff_spec.rb | 20 +++++-- .../doc_auth/redo_document_capture_spec.rb | 7 +-- spec/features/idv/step_up_spec.rb | 6 +- 12 files changed, 116 insertions(+), 43 deletions(-) diff --git a/app/controllers/idv/agreement_controller.rb b/app/controllers/idv/agreement_controller.rb index 7cb7df4591d..8d96f299562 100644 --- a/app/controllers/idv/agreement_controller.rb +++ b/app/controllers/idv/agreement_controller.rb @@ -9,6 +9,7 @@ class AgreementController < ApplicationController def show analytics.idv_doc_auth_agreement_visited(**analytics_arguments) + Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( 'agreement', :view, true diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index ca439f51379..90a57fad895 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -63,7 +63,8 @@ def self.step_info idv_session.flow_path == 'standard' && ( # mobile idv_session.skip_hybrid_handoff || - !idv_session.selfie_check_required # desktop but selfie not required + !idv_session.selfie_check_required || # desktop but selfie not required + idv_session.desktop_selfie_test_mode_enabled? ) }, undo_step: ->(idv_session:, user:) do diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index f75a238464f..0fb5e485db3 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -50,7 +50,8 @@ def self.step_info next_steps: [:link_sent, :document_capture], preconditions: ->(idv_session:, user:) { idv_session.idv_consent_given && - self.selected_remote(idv_session: idv_session) + (self.selected_remote(idv_session: idv_session) || + idv_session.desktop_selfie_test_mode_enabled?) }, undo_step: ->(idv_session:, user:) do idv_session.flow_path = nil diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb index 94a90c02b41..9a9c7db6171 100644 --- a/app/services/idv/session.rb +++ b/app/services/idv/session.rb @@ -250,6 +250,10 @@ def skip_hybrid_handoff? !!session[:skip_hybrid_handoff] end + def desktop_selfie_test_mode_enabled? + IdentityConfig.store.doc_auth_selfie_desktop_test_mode + end + private attr_accessor :user_session diff --git a/config/application.yml.default b/config/application.yml.default index de960e9aef3..dd731a015d4 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -88,6 +88,7 @@ doc_auth_max_attempts: 5 doc_auth_max_capture_attempts_before_native_camera: 3 doc_auth_max_submission_attempts_before_native_camera: 3 doc_auth_selfie_capture_enabled: false +doc_auth_selfie_desktop_test_mode: false doc_auth_sdk_capture_orientation: '{"horizontal": 100, "vertical": 0}' doc_auth_supported_country_codes: '["US", "GU", "VI", "AS", "MP", "PR", "USA" ,"GUM", "VIR", "ASM", "MNP", "PRI"]' doc_capture_request_valid_for_minutes: 15 @@ -385,6 +386,7 @@ development: database_worker_jobs_password: '' doc_auth_exit_question_section_enabled: false doc_auth_selfie_capture_enabled: false + doc_auth_selfie_desktop_test_mode: false doc_auth_vendor: 'mock' doc_auth_vendor_randomize: false doc_auth_vendor_randomize_percent: 0 @@ -527,6 +529,7 @@ test: database_worker_jobs_password: '' doc_auth_max_attempts: 4 doc_auth_selfie_capture_enabled: false + doc_auth_selfie_desktop_test_mode: false doc_auth_vendor: 'mock' doc_auth_vendor_randomize: false doc_auth_vendor_randomize_percent: 0 diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 38a3b4cd0c3..d02f087cebb 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -187,6 +187,7 @@ def self.build_store(config_map) config.add(:doc_auth_max_submission_attempts_before_native_camera, type: :integer) config.add(:doc_auth_s3_request_timeout, type: :integer) config.add(:doc_auth_selfie_capture_enabled, type: :boolean) + config.add(:doc_auth_selfie_desktop_test_mode, type: :boolean) config.add(:doc_auth_sdk_capture_orientation, type: :json, options: { symbolize_names: true }) config.add(:doc_auth_supported_country_codes, type: :json) config.add(:doc_auth_vendor, type: :string) diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 3a810533826..26c26cd3e0e 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -111,19 +111,42 @@ context 'when a selfie is requested' do let(:doc_auth_selfie_capture_enabled) { true } let(:sp_selfie_enabled) { true } + let(:desktop_selfie_enabled) { false } + before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). + and_return(desktop_selfie_enabled) + end + describe 'when desktop selfie disabled' do + let(:desktop_selfie_enabled) { false } + it 'redirect back to handoff page' do + expect(subject).not_to receive(:render).with( + :show, + locals: hash_including( + document_capture_session_uuid: document_capture_session_uuid, + doc_auth_selfie_capture: true, + ), + ).and_call_original - it 'redirect back to handoff page' do - expect(subject).not_to receive(:render).with( - :show, - locals: hash_including( - document_capture_session_uuid: document_capture_session_uuid, - doc_auth_selfie_capture: true, - ), - ).and_call_original + get :show - get :show + expect(response).to redirect_to(idv_hybrid_handoff_path) + end + end + + describe 'when desktop selfie enabled' do + let(:desktop_selfie_enabled) { true } + it 'allows capture' do + expect(subject).to receive(:render).with( + :show, + locals: hash_including( + document_capture_session_uuid: document_capture_session_uuid, + doc_auth_selfie_capture: true, + ), + ).and_call_original - expect(response).to redirect_to(idv_hybrid_handoff_path) + get :show + expect(response).to render_template :show + end end end diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index 2f5dc6626ff..f65b56f2e3f 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -846,6 +846,9 @@ def wait_for_event(event, wait) to receive(:biometric_comparison_required?). and_return(true) + allow(IdentityConfig.store).to receive(:des). + and_return(true) + allow_any_instance_of(DocAuth::Response).to receive(:selfie_status).and_return(:success) allow_any_instance_of(DocumentCaptureSessionResult). to receive(:selfie_status).and_return(:success) @@ -854,12 +857,6 @@ def wait_for_event(event, wait) allow(BrowserCache).to receive(:parse).and_return(mobile_device) allow(BrowserCache).to receive(:get).and_return(mobile_device) - # mock mobile device as cameraCapable - allow_any_instance_of(ActionController::Parameters). - to receive(:[]).and_wrap_original do |impl, param_name| - param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) - end - perform_in_browser(:mobile) do sign_in_and_2fa_user(user) visit_idp_from_sp_with_ial2(:oidc, biometric_comparison_required: true) diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index ded4cbea9a9..622cfa7e82d 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -465,17 +465,54 @@ end end end - end - context 'on desktop' do - it 'cannot proceed to document capture page' do - perform_in_browser(:desktop) do - visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_hybrid_handoff_step - # we still have option to continue - expect(page).to have_current_path(idv_hybrid_handoff_path) - click_on t('forms.buttons.upload_photos') - expect(page).to have_current_path(idv_hybrid_handoff_path) + context 'on desktop' do + let(:desktop_selfie_mode) { false } + before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). + and_return(desktop_selfie_mode) + end + describe 'when desktop selfie not allowed' do + it 'cannot proceed to document capture page' do + perform_in_browser(:desktop) do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_hybrid_handoff_step + # we still have option to continue + expect(page).to have_current_path(idv_hybrid_handoff_path) + click_on t('forms.buttons.upload_photos') + expect(page).to have_current_path(idv_hybrid_handoff_path) + end + end + end + describe 'when desktop selfie is allowed' do + let(:desktop_selfie_mode) { true } + it 'proceed to the next page with valid info, including a selfie image' do + perform_in_browser(:desktop) do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_hybrid_handoff_step + # we still have option to continue on handoff, since it's desktop no skip_hand_off + expect(page).to have_current_path(idv_hybrid_handoff_path) + click_on t('forms.buttons.upload_photos') + expect(page).to have_current_path(idv_document_capture_url) + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + expect_doc_capture_page_header(t('doc_auth.headings.document_capture_with_selfie')) + expect_doc_capture_id_subheader + expect_doc_capture_selfie_subheader + attach_liveness_images + submit_images + + expect(page).to have_current_path(idv_ssn_url) + expect_costing_for_document + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + + expect(page).to have_current_path(idv_ssn_url) + fill_out_ssn_form_ok + click_idv_continue + complete_verify_step + expect(page).to have_current_path(idv_phone_url) + end + end end end end diff --git a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb index 3c99f3f304e..166cf454b4d 100644 --- a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb +++ b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb @@ -209,7 +209,10 @@ context 'on a desktop device when selfie required', js: true do let(:user) { user_with_2fa } + let(:desktop_selfie_enabled) { false } before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). + and_return(desktop_selfie_enabled) expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once). and_return(true) sign_in_and_2fa_user(user) @@ -219,10 +222,19 @@ and_return(fake_attempts_tracker) complete_doc_auth_steps_before_document_capture_step end - it 'it prevents from proceeding to document capture' do - expect(page).to have_current_path(idv_hybrid_handoff_path) - click_on t('forms.buttons.upload_photos') - expect(page).to have_current_path(idv_hybrid_handoff_path) + describe 'with desktop selfie disabled' do + let(:desktop_selfie_enabled) { false } + it 'it prevents from proceeding to document capture' do + expect(page).to have_current_path(idv_hybrid_handoff_path) + click_on t('forms.buttons.upload_photos') + expect(page).to have_current_path(idv_hybrid_handoff_path) + end + end + describe 'with desktop selfie enabled' do + let(:desktop_selfie_enabled) { true } + it 'it proceeds to document capture' do + expect(page).to have_current_path(idv_document_capture_path) + end end end end diff --git a/spec/features/idv/doc_auth/redo_document_capture_spec.rb b/spec/features/idv/doc_auth/redo_document_capture_spec.rb index 3b756633c77..47bcdf6e856 100644 --- a/spec/features/idv/doc_auth/redo_document_capture_spec.rb +++ b/spec/features/idv/doc_auth/redo_document_capture_spec.rb @@ -277,11 +277,8 @@ context 'when selfie is enabled' do before do - # mock mobile device as cameraCapable - allow_any_instance_of(ActionController::Parameters). - to receive(:[]).and_wrap_original do |impl, param_name| - param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) - end + # allow selfie on desktop + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(true) end context 'error due to data issue with 2xx status code', allow_browser_log: true do before do diff --git a/spec/features/idv/step_up_spec.rb b/spec/features/idv/step_up_spec.rb index 77de66e3b28..085e20b4f6c 100644 --- a/spec/features/idv/step_up_spec.rb +++ b/spec/features/idv/step_up_spec.rb @@ -13,11 +13,7 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_selfie_capture_enabled).and_return(true) - # mock mobile device as cameraCapable - allow_any_instance_of(ActionController::Parameters). - to receive(:[]).and_wrap_original do |impl, param_name| - param_name.to_sym == :skip_hybrid_handoff ? '' : impl.call(param_name) - end + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(true) end scenario 'User with active profile can redo idv when selfie required', js: true do From a1f7142e959d34da257b9ea178183c462568be09 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 23 Feb 2024 11:19:33 -0500 Subject: [PATCH 19/27] LG-12306: fix test. --- .../idv/doc_auth/document_capture_spec.rb | 304 ++++++++++-------- 1 file changed, 167 insertions(+), 137 deletions(-) diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index 622cfa7e82d..05b59a6488b 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -277,122 +277,151 @@ expect(page).to have_current_path(idv_phone_url) end end - - context 'selfie with no liveness or poor quality is uploaded', allow_browser_log: true do - it 'try again and page show no liveness inline error message' do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_no_liveness.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_no_liveness.yml' - ), - ) - submit_images - message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) - expect(page).to have_content(message) - detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live')) - security_message = strip_tags( - t( - 'idv.warning.attempts_html', - count: IdentityConfig.store.doc_auth_max_attempts - 1, - ), - ) - expect(page).to have_content(detail_message << "\n" << security_message) - review_issues_header = strip_tags( - t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), - ) - expect(page).to have_content(review_issues_header) - expect(page).to have_current_path(idv_document_capture_path) - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(inline_error) + context 'when a selfie is required by SP', allow_browser_log: true do + before do + allow_any_instance_of(FederatedProtocols::Oidc). + to receive(:biometric_comparison_required?). + and_return({ biometric_comparison_required: true }) end + it 'proceeds to the next page with valid info, including a selfie image' do + perform_in_browser(:mobile) do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step - it 'try again and page show poor quality inline error message' do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_poor_quality.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_credential_poor_quality.yml' - ), - ) - submit_images - message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) - expect(page).to have_content(message) - detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) - security_message = strip_tags( - t( - 'idv.warning.attempts_html', - count: IdentityConfig.store.doc_auth_max_attempts - 1, - ), - ) - expect(page).to have_content(detail_message << "\n" << security_message) - review_issues_header = strip_tags( - t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), - ) - expect(page).to have_content(review_issues_header) - expect(page).to have_current_path(idv_document_capture_path) - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(inline_error) - end + expect(page).to have_current_path(idv_document_capture_url) + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + expect_doc_capture_page_header(t('doc_auth.headings.document_capture_with_selfie')) + expect_doc_capture_id_subheader + expect_doc_capture_selfie_subheader + attach_liveness_images + submit_images - it 'try again and page show selfie fail inline error message' do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - attach_images( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_portrait_match_failure.yml' - ), - ) - attach_selfie( - Rails.root.join( - 'spec', 'fixtures', - 'ial2_test_portrait_match_failure.yml' - ), - ) - submit_images - message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) - expect(page).to have_content(message) - detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) - security_message = strip_tags( - t( - 'idv.warning.attempts_html', - count: IdentityConfig.store.doc_auth_max_attempts - 1, - ), - ) - expect(page).to have_content(detail_message << "\n" << security_message) - review_issues_header = strip_tags( - t('errors.doc_auth.selfie_fail_heading'), - ) - expect(page).to have_content(review_issues_header) - expect(page).to have_current_path(idv_document_capture_path) - click_try_again - expect(page).to have_current_path(idv_document_capture_path) - inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) - expect(page).to have_content(inline_error) + expect(page).to have_current_path(idv_ssn_url) + expect_costing_for_document + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + + expect(page).to have_current_path(idv_ssn_url) + fill_out_ssn_form_ok + click_idv_continue + complete_verify_step + expect(page).to have_current_path(idv_phone_url) + end end + context 'selfie with no liveness or poor quality is uploaded', allow_browser_log: true do + it 'try again and page show no liveness inline error message' do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_no_liveness.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_no_liveness.yml' + ), + ) + submit_images + message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end + it 'try again and page show poor quality inline error message' do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_poor_quality.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_poor_quality.yml' + ), + ) + submit_images + message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end + it 'try again and page show selfie fail inline error message' do + visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_portrait_match_failure.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_portrait_match_failure.yml' + ), + ) + submit_images + message = strip_tags(t('errors.doc_auth.selfie_fail_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_poor_quality')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_fail_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end + end context 'with Attention with Barcode' do it 'try again and page show selfie fail inline error message' do visit_idp_from_oidc_sp_with_ial2 @@ -435,32 +464,33 @@ end end - context 'when selfie check is not enabled (flag off, and/or in production)' do - let(:selfie_check_enabled) { false } - it 'proceeds to the next page with valid info, excluding a selfie image' do - perform_in_browser(:mobile) do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - - expect(page).to have_current_path(idv_document_capture_url) - expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - - expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) - - expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - attach_images - submit_images - - expect(page).to have_current_path(idv_ssn_url) - expect_costing_for_document - expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') - - expect(page).to have_current_path(idv_ssn_url) - fill_out_ssn_form_ok - click_idv_continue - complete_verify_step - expect(page).to have_current_path(idv_phone_url) + context 'when selfie check is not enabled (flag off, and/or in production)' do + let(:selfie_check_enabled) { false } + it 'proceeds to the next page with valid info, excluding a selfie image' do + perform_in_browser(:mobile) do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + + expect(page).to have_current_path(idv_document_capture_url) + expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) + + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + + expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) + attach_images + submit_images + + expect(page).to have_current_path(idv_ssn_url) + expect_costing_for_document + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + + expect(page).to have_current_path(idv_ssn_url) + fill_out_ssn_form_ok + click_idv_continue + complete_verify_step + expect(page).to have_current_path(idv_phone_url) + end end end end From dd0547bc2d10616e4b15cbb74e05f44e4655388d Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Fri, 23 Feb 2024 13:18:01 -0500 Subject: [PATCH 20/27] LG-12306: restore events since we now allow selfie in test mode. --- spec/features/idv/analytics_spec.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index f65b56f2e3f..2825ffa74f0 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -486,6 +486,12 @@ 'IdV: doc auth agreement submitted' => { success: true, errors: {}, step: 'agreement', analytics_id: 'Doc Auth', skip_hybrid_handoff: anything, irs_reproofing: false, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default }, + 'IdV: doc auth hybrid handoff visited' => { + step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean + }, + 'IdV: doc auth hybrid handoff submitted' => { + success: true, errors: {}, destination: :document_capture, flow_path: 'standard', step: 'hybrid_handoff', redo_document_capture: nil, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', skip_hybrid_handoff: nil, irs_reproofing: false, selfie_check_required: boolean + }, 'IdV: doc auth document_capture visited' => { flow_path: 'standard', step: 'document_capture', redo_document_capture: nil, skip_hybrid_handoff: anything, acuant_sdk_upgrade_ab_test_bucket: :default, lexisnexis_instant_verify_workflow_ab_test_bucket: :default, analytics_id: 'Doc Auth', irs_reproofing: false, selfie_check_required: boolean }, @@ -853,15 +859,12 @@ def wait_for_event(event, wait) allow_any_instance_of(DocumentCaptureSessionResult). to receive(:selfie_status).and_return(:success) - mobile_device = Browser.new(mobile_user_agent) - allow(BrowserCache).to receive(:parse).and_return(mobile_device) - allow(BrowserCache).to receive(:get).and_return(mobile_device) + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(true) - perform_in_browser(:mobile) do + perform_in_browser(:desktop) do sign_in_and_2fa_user(user) visit_idp_from_sp_with_ial2(:oidc, biometric_comparison_required: true) complete_doc_auth_steps_before_document_capture_step - attach_images attach_selfie submit_images From a2f35d2669a550505b9717048554dd959e5adf5f Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Mon, 26 Feb 2024 12:28:13 -0500 Subject: [PATCH 21/27] LG-12306: set the flag to true in dev and test. --- config/application.yml.default | 4 ++-- spec/controllers/idv/document_capture_controller_spec.rb | 4 ++++ spec/controllers/idv/hybrid_handoff_controller_spec.rb | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/config/application.yml.default b/config/application.yml.default index dd731a015d4..c129c8db3e2 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -386,7 +386,7 @@ development: database_worker_jobs_password: '' doc_auth_exit_question_section_enabled: false doc_auth_selfie_capture_enabled: false - doc_auth_selfie_desktop_test_mode: false + doc_auth_selfie_desktop_test_mode: true doc_auth_vendor: 'mock' doc_auth_vendor_randomize: false doc_auth_vendor_randomize_percent: 0 @@ -529,7 +529,7 @@ test: database_worker_jobs_password: '' doc_auth_max_attempts: 4 doc_auth_selfie_capture_enabled: false - doc_auth_selfie_desktop_test_mode: false + doc_auth_selfie_desktop_test_mode: true doc_auth_vendor: 'mock' doc_auth_vendor_randomize: false doc_auth_vendor_randomize_percent: 0 diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 26c26cd3e0e..797dfa82421 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -54,6 +54,10 @@ end describe 'with sp selfie enabled' do let(:sp_selfie_enabled) { true } + before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). + and_return(false) + end it 'does satisfy precondition' do expect(Idv::DocumentCaptureController.step_info.preconditions.is_a?(Proc)) expect(subject).not_to receive(:render).with(:show, locals: an_instance_of(Hash)) diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb index 4271313d830..8efff18e1cd 100644 --- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb +++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb @@ -209,6 +209,8 @@ context 'opt in selection is nil' do before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). + and_return(false) subject.idv_session.skip_doc_auth = nil end @@ -230,6 +232,8 @@ context 'opted in to ipp flow' do before do + allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). + and_return(false) subject.idv_session.skip_doc_auth = true end From 630ce74e9f99e79dbfea8ab3d1fa739406fad110 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Mon, 26 Feb 2024 16:29:54 -0500 Subject: [PATCH 22/27] LG-12306: try remove the flag. --- app/controllers/idv/hybrid_handoff_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 0fb5e485db3..4b6cd3e5932 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -50,8 +50,7 @@ def self.step_info next_steps: [:link_sent, :document_capture], preconditions: ->(idv_session:, user:) { idv_session.idv_consent_given && - (self.selected_remote(idv_session: idv_session) || - idv_session.desktop_selfie_test_mode_enabled?) + self.selected_remote(idv_session: idv_session) }, undo_step: ->(idv_session:, user:) do idv_session.flow_path = nil From 21e1da04d0cc9026000293e64d0ca80351f83259 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 27 Feb 2024 09:15:25 -0500 Subject: [PATCH 23/27] LG-12306: rebase to main. --- .../idv/hybrid_handoff_controller.rb | 2 +- .../idv/doc_auth/document_capture_spec.rb | 56 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index 4b6cd3e5932..f75a238464f 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -50,7 +50,7 @@ def self.step_info next_steps: [:link_sent, :document_capture], preconditions: ->(idv_session:, user:) { idv_session.idv_consent_given && - self.selected_remote(idv_session: idv_session) + self.selected_remote(idv_session: idv_session) }, undo_step: ->(idv_session:, user:) do idv_session.flow_path = nil diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index 05b59a6488b..a5f541cec5b 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -308,7 +308,8 @@ expect(page).to have_current_path(idv_phone_url) end end - context 'selfie with no liveness or poor quality is uploaded', allow_browser_log: true do + context 'selfie with no liveness or poor quality is uploaded', + allow_browser_log: true do it 'try again and page show no liveness inline error message' do visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true) sign_in_and_2fa_user(user) @@ -464,33 +465,32 @@ end end - context 'when selfie check is not enabled (flag off, and/or in production)' do - let(:selfie_check_enabled) { false } - it 'proceeds to the next page with valid info, excluding a selfie image' do - perform_in_browser(:mobile) do - visit_idp_from_oidc_sp_with_ial2 - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_document_capture_step - - expect(page).to have_current_path(idv_document_capture_url) - expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - - expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) - - expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) - attach_images - submit_images - - expect(page).to have_current_path(idv_ssn_url) - expect_costing_for_document - expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') - - expect(page).to have_current_path(idv_ssn_url) - fill_out_ssn_form_ok - click_idv_continue - complete_verify_step - expect(page).to have_current_path(idv_phone_url) - end + context 'when selfie check is not enabled (flag off, and/or in production)' do + let(:selfie_check_enabled) { false } + it 'proceeds to the next page with valid info, excluding a selfie image' do + perform_in_browser(:mobile) do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + + expect(page).to have_current_path(idv_document_capture_url) + expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) + + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + + expect(page).not_to have_content(t('doc_auth.headings.document_capture_selfie')) + attach_images + submit_images + + expect(page).to have_current_path(idv_ssn_url) + expect_costing_for_document + expect(DocAuthLog.find_by(user_id: user.id).state).to eq('MT') + + expect(page).to have_current_path(idv_ssn_url) + fill_out_ssn_form_ok + click_idv_continue + complete_verify_step + expect(page).to have_current_path(idv_phone_url) end end end From e5940cfde991dcbf23a2962d6cd4d2a5302218ef Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 27 Feb 2024 10:37:19 -0500 Subject: [PATCH 24/27] LG-12306: clean up. --- .../idv/doc_auth/hybrid_handoff_spec.rb | 42 ++++--------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb index 166cf454b4d..53f1bbd5248 100644 --- a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb +++ b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb @@ -12,12 +12,15 @@ IdentityConfig.store.idv_send_link_attempt_window_in_minutes end + before do + sign_in_and_2fa_user + allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) + allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker). + and_return(fake_attempts_tracker) + end + context 'on a desktop device send link' do before do - sign_in_and_2fa_user - allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) - allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker). - and_return(fake_attempts_tracker) complete_doc_auth_steps_before_hybrid_handoff_step end @@ -206,35 +209,4 @@ expect(document_capture_session).to have_attributes(requested_at: a_kind_of(Time)) end end - - context 'on a desktop device when selfie required', js: true do - let(:user) { user_with_2fa } - let(:desktop_selfie_enabled) { false } - before do - allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode). - and_return(desktop_selfie_enabled) - expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once). - and_return(true) - sign_in_and_2fa_user(user) - visit_idp_from_sp_with_ial2(:oidc, biometric_comparison_required: true) - allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) - allow_any_instance_of(ApplicationController).to receive(:irs_attempts_api_tracker). - and_return(fake_attempts_tracker) - complete_doc_auth_steps_before_document_capture_step - end - describe 'with desktop selfie disabled' do - let(:desktop_selfie_enabled) { false } - it 'it prevents from proceeding to document capture' do - expect(page).to have_current_path(idv_hybrid_handoff_path) - click_on t('forms.buttons.upload_photos') - expect(page).to have_current_path(idv_hybrid_handoff_path) - end - end - describe 'with desktop selfie enabled' do - let(:desktop_selfie_enabled) { true } - it 'it proceeds to document capture' do - expect(page).to have_current_path(idv_document_capture_path) - end - end - end end From f538ae290d8ac1a65be9afdbc3a99ed5656b2ca9 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 27 Feb 2024 10:51:42 -0500 Subject: [PATCH 25/27] LG-12306: clean up. --- spec/features/idv/analytics_spec.rb | 2 -- spec/features/idv/doc_auth/redo_document_capture_spec.rb | 4 ---- spec/features/idv/step_up_spec.rb | 1 - 3 files changed, 7 deletions(-) diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index 2825ffa74f0..d9ddfd32719 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -859,8 +859,6 @@ def wait_for_event(event, wait) allow_any_instance_of(DocumentCaptureSessionResult). to receive(:selfie_status).and_return(:success) - allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(true) - perform_in_browser(:desktop) do sign_in_and_2fa_user(user) visit_idp_from_sp_with_ial2(:oidc, biometric_comparison_required: true) diff --git a/spec/features/idv/doc_auth/redo_document_capture_spec.rb b/spec/features/idv/doc_auth/redo_document_capture_spec.rb index 47bcdf6e856..0916b126bbd 100644 --- a/spec/features/idv/doc_auth/redo_document_capture_spec.rb +++ b/spec/features/idv/doc_auth/redo_document_capture_spec.rb @@ -276,10 +276,6 @@ end context 'when selfie is enabled' do - before do - # allow selfie on desktop - allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(true) - end context 'error due to data issue with 2xx status code', allow_browser_log: true do before do expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once). diff --git a/spec/features/idv/step_up_spec.rb b/spec/features/idv/step_up_spec.rb index 085e20b4f6c..3085068aa52 100644 --- a/spec/features/idv/step_up_spec.rb +++ b/spec/features/idv/step_up_spec.rb @@ -13,7 +13,6 @@ before do allow(IdentityConfig.store).to receive(:doc_auth_selfie_capture_enabled).and_return(true) - allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(true) end scenario 'User with active profile can redo idv when selfie required', js: true do From 703ba278a1d0603ec2cba675e14f348988fd51ad Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 27 Feb 2024 10:57:57 -0500 Subject: [PATCH 26/27] LG-12306: clean up. --- spec/features/idv/doc_auth/document_capture_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index a5f541cec5b..0e2d7451601 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -281,7 +281,7 @@ before do allow_any_instance_of(FederatedProtocols::Oidc). to receive(:biometric_comparison_required?). - and_return({ biometric_comparison_required: true }) + and_return(true) end it 'proceeds to the next page with valid info, including a selfie image' do perform_in_browser(:mobile) do From d746033d628dcd716a518cbaa05dd4dc16949847 Mon Sep 17 00:00:00 2001 From: Dawei Wang Date: Tue, 27 Feb 2024 11:17:42 -0500 Subject: [PATCH 27/27] LG-12306: clean up. --- spec/features/idv/analytics_spec.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/features/idv/analytics_spec.rb b/spec/features/idv/analytics_spec.rb index d9ddfd32719..138a09de87c 100644 --- a/spec/features/idv/analytics_spec.rb +++ b/spec/features/idv/analytics_spec.rb @@ -851,10 +851,6 @@ def wait_for_event(event, wait) allow_any_instance_of(FederatedProtocols::Oidc). to receive(:biometric_comparison_required?). and_return(true) - - allow(IdentityConfig.store).to receive(:des). - and_return(true) - allow_any_instance_of(DocAuth::Response).to receive(:selfie_status).and_return(:success) allow_any_instance_of(DocumentCaptureSessionResult). to receive(:selfie_status).and_return(:success)