From 292715f5371967be08d18f61ff147480af0d9672 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Tue, 18 Feb 2025 18:51:57 -0500 Subject: [PATCH 1/5] verify docv id type changelog: Bug Fixes, Document Authentication, Validate DocV state ID type --- .../socure/responses/docv_result_response.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/services/doc_auth/socure/responses/docv_result_response.rb b/app/services/doc_auth/socure/responses/docv_result_response.rb index 2f62a05fce5..01403adba2e 100644 --- a/app/services/doc_auth/socure/responses/docv_result_response.rb +++ b/app/services/doc_auth/socure/responses/docv_result_response.rb @@ -37,6 +37,11 @@ class DocvResultResponse < DocAuth::Response socure_user_id: %w[customerProfile userId], }.freeze + STATE_ID_TYPES_PERMITTED = [ + 'state_id_card', + 'drivers_license', + ].freeze + def initialize(http_response:, biometric_comparison_required: false) @http_response = http_response @@ -44,7 +49,7 @@ def initialize(http_response:, @pii_from_doc = read_pii super( - success: successful_result?, + success: doc_auth_success?, errors: error_messages, pii_from_doc:, extra: extra_attributes, @@ -62,7 +67,7 @@ def initialize(http_response:, end def doc_auth_success? - success? + successful_result? && id_type_supported? end def selfie_status @@ -84,7 +89,7 @@ def extra_attributes flow_path: nil, liveness_checking_required: @biometric_comparison_required, issue_year: state_id_issued&.year, - doc_auth_success: successful_result?, + doc_auth_success: doc_auth_success?, vendor: 'Socure', # TODO: Replace with Idp::Constants::Vendors::SOCURE address_line2_present: address2.present?, zip_code: zipcode, @@ -180,6 +185,10 @@ def parse_date(date_string) Rails.logger.info(message) nil end + + def id_type_supported? + STATE_ID_TYPES_PERMITTED.include? state_id_type + end end end end From a16cfe7417dc249c181c663822476e979f7a52e0 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Tue, 18 Feb 2025 19:43:54 -0500 Subject: [PATCH 2/5] use existing ID TYPE SLUGS --- .../socure/responses/docv_result_response.rb | 16 +++++++--------- spec/jobs/socure_docv_results_job_spec.rb | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/app/services/doc_auth/socure/responses/docv_result_response.rb b/app/services/doc_auth/socure/responses/docv_result_response.rb index 01403adba2e..cbcb9fcc6fd 100644 --- a/app/services/doc_auth/socure/responses/docv_result_response.rb +++ b/app/services/doc_auth/socure/responses/docv_result_response.rb @@ -37,11 +37,6 @@ class DocvResultResponse < DocAuth::Response socure_user_id: %w[customerProfile userId], }.freeze - STATE_ID_TYPES_PERMITTED = [ - 'state_id_card', - 'drivers_license', - ].freeze - def initialize(http_response:, biometric_comparison_required: false) @http_response = http_response @@ -85,7 +80,7 @@ def extra_attributes reason_codes: get_data(DATA_PATHS[:reason_codes]), document_type: get_data(DATA_PATHS[:document_type]), state: state, - state_id_type: state_id_type, + state_id_type:, flow_path: nil, liveness_checking_required: @biometric_comparison_required, issue_year: state_id_issued&.year, @@ -164,8 +159,11 @@ def state_id_issued end def state_id_type - type = get_data(DATA_PATHS[:id_type]) - type&.gsub(/\W/, '')&.underscore + document_id_type&.gsub(/\W/, '')&.underscore + end + + def document_id_type + get_data(DATA_PATHS[:id_type]) end def dob @@ -187,7 +185,7 @@ def parse_date(date_string) end def id_type_supported? - STATE_ID_TYPES_PERMITTED.include? state_id_type + DocAuth::Response::ID_TYPE_SLUGS.key?(document_id_type) end end end diff --git a/spec/jobs/socure_docv_results_job_spec.rb b/spec/jobs/socure_docv_results_job_spec.rb index 8ad61700fe3..3e092c6e419 100644 --- a/spec/jobs/socure_docv_results_job_spec.rb +++ b/spec/jobs/socure_docv_results_job_spec.rb @@ -15,6 +15,7 @@ let(:socure_idplus_base_url) { 'https://example.com' } let(:decision_value) { 'accept' } let(:expiration_date) { "#{1.year.from_now.year}-01-01" } + let(:document_type_type) { 'Drivers License' } before do allow(IdentityConfig.store).to receive(:socure_idplus_base_url) @@ -40,7 +41,7 @@ documentVerification: { reasonCodes: %w[I831 R810], documentType: { - type: 'Drivers License', + type: document_type_type, country: 'USA', state: 'NY', }, @@ -85,7 +86,7 @@ zip_code: '10001', doc_auth_success: true, document_type: { - type: 'Drivers License', + type: document_type_type, country: 'USA', state: 'NY', }, @@ -114,6 +115,20 @@ expect(document_capture_session_result.selfie_status).to eq(:not_processed) end + context 'not accepted document type' do + let(:document_type_type) { 'Passport' } + it 'doc auth fails' do + perform + + document_capture_session.reload + document_capture_session_result = document_capture_session.load_result + expect(document_capture_session_result.success).to eq(false) + expect(document_capture_session_result.pii[:first_name]).to eq('Dwayne') + expect(document_capture_session_result.attention_with_barcode).to eq(false) + expect(document_capture_session_result.doc_auth_success).to eq(false) + expect(document_capture_session_result.selfie_status).to eq(:not_processed) + end + end context 'Socure returns an error' do let(:status) { 'Error' } let(:referenceId) { '360ae43f-123f-47ab-8e05-6af79752e76c' } From f29cb7a4dfa18373027d056cee569fa13cacda85 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Tue, 18 Feb 2025 23:18:21 -0500 Subject: [PATCH 3/5] display invalid id type error messaging --- .../concerns/idv/document_capture_concern.rb | 1 + .../concerns/idv/socure_errors_concern.rb | 4 +++- .../idv/socure/errors_controller.rb | 4 +++- app/presenters/socure_error_presenter.rb | 4 ++++ .../socure/responses/docv_result_response.rb | 6 ++++-- .../doc_auth/socure_document_capture_spec.rb | 21 +++++++++++++++++++ 6 files changed, 36 insertions(+), 4 deletions(-) diff --git a/app/controllers/concerns/idv/document_capture_concern.rb b/app/controllers/concerns/idv/document_capture_concern.rb index 0c71c24c7f3..18f319bfa17 100644 --- a/app/controllers/concerns/idv/document_capture_concern.rb +++ b/app/controllers/concerns/idv/document_capture_concern.rb @@ -34,6 +34,7 @@ def error_hash(message) message: message || I18n.t('doc_auth.errors.general.network_error'), socure: stored_result&.errors&.dig(:socure), pii_validation: stored_result&.errors&.dig(:pii_validation), + unaccepted_id_type: stored_result&.errors&.dig(:unaccepted_id_type), } end diff --git a/app/controllers/concerns/idv/socure_errors_concern.rb b/app/controllers/concerns/idv/socure_errors_concern.rb index 76d2b8eeb3f..a47c6a9498b 100644 --- a/app/controllers/concerns/idv/socure_errors_concern.rb +++ b/app/controllers/concerns/idv/socure_errors_concern.rb @@ -12,7 +12,9 @@ def remaining_attempts end def error_code_for(result) - if result.errors[:socure] + if result.errors[:unaccepted_id_type] + :unaccepted_id_type + elsif result.errors[:socure] result.errors.dig(:socure, :reason_codes).first elsif result.errors[:network] :network diff --git a/app/controllers/idv/socure/errors_controller.rb b/app/controllers/idv/socure/errors_controller.rb index 7d3259896e4..0f57a13479f 100644 --- a/app/controllers/idv/socure/errors_controller.rb +++ b/app/controllers/idv/socure/errors_controller.rb @@ -69,7 +69,9 @@ def socure_errors_presenter(error_code) end def error_code_for(result) - if result.errors[:socure] + if result.errors[:unaccepted_id_type] + :unaccepted_id_type + elsif result.errors[:socure] result.errors.dig(:socure, :reason_codes).first elsif result.errors[:network] :network diff --git a/app/presenters/socure_error_presenter.rb b/app/presenters/socure_error_presenter.rb index 814ffdb98a9..f6f687a27de 100644 --- a/app/presenters/socure_error_presenter.rb +++ b/app/presenters/socure_error_presenter.rb @@ -144,6 +144,8 @@ def heading_string_for(error_code) t('doc_auth.headers.general.network_error') when :timeout, :url_not_found t('idv.errors.technical_difficulties') + when :unaccepted_id_type + t('doc_auth.headers.unaccepted_id_type') else # i18n-tasks-use t('doc_auth.headers.unreadable_id') # i18n-tasks-use t('doc_auth.headers.unaccepted_id_type') @@ -161,6 +163,8 @@ def error_string_for(error_code) t('doc_auth.errors.general.new_network_error') when :timeout, :url_not_found t('idv.errors.try_again_later') + when :unaccepted_id_type + t('doc_auth.errors.unaccepted_id_type') else if remapped_error(error_code) == 'underage' # special handling because it says 'Login.gov' I18n.t('doc_auth.errors.underage', app_name: APP_NAME) diff --git a/app/services/doc_auth/socure/responses/docv_result_response.rb b/app/services/doc_auth/socure/responses/docv_result_response.rb index cbcb9fcc6fd..af6e50fcac3 100644 --- a/app/services/doc_auth/socure/responses/docv_result_response.rb +++ b/app/services/doc_auth/socure/responses/docv_result_response.rb @@ -62,7 +62,7 @@ def initialize(http_response:, end def doc_auth_success? - successful_result? && id_type_supported? + id_type_supported? && successful_result? end def selfie_status @@ -100,7 +100,9 @@ def successful_result? end def error_messages - if !successful_result? + if !id_type_supported? + { unaccepted_id_type: 'failed' } + elsif !successful_result? { socure: { reason_codes: get_data(DATA_PATHS[:reason_codes]) } } else {} diff --git a/spec/features/idv/doc_auth/socure_document_capture_spec.rb b/spec/features/idv/doc_auth/socure_document_capture_spec.rb index 98d366af79b..11c0c79a174 100644 --- a/spec/features/idv/doc_auth/socure_document_capture_spec.rb +++ b/spec/features/idv/doc_auth/socure_document_capture_spec.rb @@ -388,6 +388,27 @@ end end end + + context 'not accepted id type' do + it 'displays unaccepdted id type error message' do + body = JSON.parse(SocureDocvFixtures.pass_json) + body['documentVerification']['documentType']['type'] = 'Passport' + + remove_request_stub(@pass_stub) + stub_docv_verification_data( + docv_transaction_token: @docv_transaction_token, + body: body.to_json, + ) + + socure_docv_upload_documents( + docv_transaction_token: @docv_transaction_token, + ) + visit idv_socure_document_capture_update_path + + expect(page).to have_content(t('doc_auth.headers.unaccepted_id_type')) + expect(page).to have_content(t('doc_auth.errors.unaccepted_id_type')) + end + end end context 'standard mobile flow' do From 66a8ce656876de2cd377f14540771df37c9c4dd9 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Tue, 18 Feb 2025 23:34:47 -0500 Subject: [PATCH 4/5] hybrid mobile test for unacceptable id type --- .../hybrid_socure_mobile_spec.rb | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb b/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb index 866794851cb..bccd24765f3 100644 --- a/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb +++ b/spec/features/idv/hybrid_mobile/hybrid_socure_mobile_spec.rb @@ -455,6 +455,55 @@ end end end + + context 'invalid ID type' do + it 'presents as an unaccepted ID type error', js: true do + user = nil + + perform_in_browser(:desktop) do + visit_idp_from_sp_with_ial2(sp) + user = sign_up_and_2fa_ial1_user + + complete_doc_auth_steps_before_hybrid_handoff_step + clear_and_fill_in(:doc_auth_phone, phone_number) + click_send_link + + expect(page).to have_content(t('doc_auth.headings.text_message')) + expect(page).to have_content(t('doc_auth.info.you_entered')) + expect(page).to have_content('+1 415-555-0199') + + # Confirm that Continue button is not shown when polling is enabled + expect(page).not_to have_content(t('doc_auth.buttons.continue')) + end + + expect(@sms_link).to be_present + + perform_in_browser(:mobile) do + visit @sms_link + + body = JSON.parse(SocureDocvFixtures.pass_json) + body['documentVerification']['documentType']['type'] = 'Passport' + remove_request_stub(@pass_stub) + stub_docv_verification_data( + docv_transaction_token: @docv_transaction_token, + body: body.to_json, + ) + + click_idv_continue + + socure_docv_upload_documents(docv_transaction_token: @docv_transaction_token) + + visit idv_hybrid_mobile_socure_document_capture_update_url + + expect(page).to have_content(t('doc_auth.headers.unaccepted_id_type')) + expect(page).to have_content(t('doc_auth.errors.unaccepted_id_type')) + end + + perform_in_browser(:desktop) do + expect(page).to have_current_path(idv_link_sent_path) + end + end + end end shared_examples 'a properly categorized Socure error' do |socure_error_code, expected_header_key| From df774c50311ada7f31f6e499195121a9cd76de0c Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Wed, 19 Feb 2025 11:11:11 -0500 Subject: [PATCH 5/5] set unnaceptable_id_type to boolean --- app/services/doc_auth/socure/responses/docv_result_response.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/doc_auth/socure/responses/docv_result_response.rb b/app/services/doc_auth/socure/responses/docv_result_response.rb index af6e50fcac3..5ab5bdca2eb 100644 --- a/app/services/doc_auth/socure/responses/docv_result_response.rb +++ b/app/services/doc_auth/socure/responses/docv_result_response.rb @@ -101,7 +101,7 @@ def successful_result? def error_messages if !id_type_supported? - { unaccepted_id_type: 'failed' } + { unaccepted_id_type: true } elsif !successful_result? { socure: { reason_codes: get_data(DATA_PATHS[:reason_codes]) } } else