From 6c28c2bbfdef36fafacae98d2f1b97922b0a7203 Mon Sep 17 00:00:00 2001 From: Sheldon Bachstein Date: Wed, 1 Mar 2023 10:20:08 -0800 Subject: [PATCH 01/12] LG-8856: Account for polling delays when sending in-person proofing emails (#7805) * Account for polling delays when sending emails changelog: User-facing Improvements, In-person proofing, reduce delay when sending proofing emails to users if their enrollment hasn't been updated in a while * Re-run migration to get comment * Add JSON merge helper method * Update how we convert to UTC timezone * Use UTC offset instead of string name * Minor cleanups * Add check for wait_until attribute * Add spec checking queue and wait_until on failure --- app/jobs/get_usps_proofing_results_job.rb | 53 +++++--- ...add_proofed_at_to_in_person_enrollments.rb | 5 + db/schema.rb | 1 + spec/factories/in_person_enrollments.rb | 1 + .../get_usps_proofing_results_job_spec.rb | 123 ++++++++++++++++-- spec/support/usps_ipp_helper.rb | 37 ++++-- 6 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 db/primary_migrate/20230206184647_add_proofed_at_to_in_person_enrollments.rb diff --git a/app/jobs/get_usps_proofing_results_job.rb b/app/jobs/get_usps_proofing_results_job.rb index a6d158d863a..16dcb4d0aec 100644 --- a/app/jobs/get_usps_proofing_results_job.rb +++ b/app/jobs/get_usps_proofing_results_job.rb @@ -18,7 +18,7 @@ def email_analytics_attributes(enrollment) timestamp: Time.zone.now, user_id: enrollment.user_id, service_provider: enrollment.issuer, - delay_time_seconds: mail_delivery_params[:wait], + wait_until: mail_delivery_params(enrollment.proofed_at)[:wait_until], } end @@ -42,8 +42,8 @@ def response_analytics_attributes(response) primary_id_type: response['primaryIdType'], secondary_id_type: response['secondaryIdType'], failure_reason: response['failureReason'], - transaction_end_date_time: response['transactionEndDateTime'], - transaction_start_date_time: response['transactionStartDateTime'], + transaction_end_date_time: parse_usps_timestamp(response['transactionEndDateTime']), + transaction_start_date_time: parse_usps_timestamp(response['transactionStartDateTime']), status: response['status'], assurance_level: response['assuranceLevel'], proofing_post_office: response['proofingPostOffice'], @@ -234,6 +234,7 @@ def handle_unsupported_status(enrollment, response) end def handle_unsupported_id_type(enrollment, response) + proofed_at = parse_usps_timestamp(response['transactionEndDateTime']) enrollment_outcomes[:enrollments_failed] += 1 analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_enrollment_updated( **enrollment_analytics_attributes(enrollment, complete: true), @@ -243,7 +244,13 @@ def handle_unsupported_id_type(enrollment, response) primary_id_type: response['primaryIdType'], reason: 'Unsupported ID type', ) - enrollment.update(status: :failed) + enrollment.update(status: :failed, proofed_at: proofed_at) + + send_failed_email(enrollment.user, enrollment) + analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_email_initiated( + **email_analytics_attributes(enrollment), + email_type: 'Failed unsupported ID type', + ) end def handle_expired_status_update(enrollment, response, response_message) @@ -301,6 +308,7 @@ def handle_unexpected_response(enrollment, response_message, reason:, cancel: tr end def handle_failed_status(enrollment, response) + proofed_at = parse_usps_timestamp(response['transactionEndDateTime']) enrollment_outcomes[:enrollments_failed] += 1 analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_enrollment_updated( **enrollment_analytics_attributes(enrollment, complete: true), @@ -309,7 +317,7 @@ def handle_failed_status(enrollment, response) reason: 'Failed status', ) - enrollment.update(status: :failed) + enrollment.update(status: :failed, proofed_at: proofed_at) if response['fraudSuspected'] send_failed_fraud_email(enrollment.user, enrollment) analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_email_initiated( @@ -326,6 +334,7 @@ def handle_failed_status(enrollment, response) end def handle_successful_status_update(enrollment, response) + proofed_at = parse_usps_timestamp(response['transactionEndDateTime']) enrollment_outcomes[:enrollments_passed] += 1 analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_enrollment_updated( **enrollment_analytics_attributes(enrollment, complete: true), @@ -334,12 +343,12 @@ def handle_successful_status_update(enrollment, response) passed: true, reason: 'Successful status update', ) + enrollment.profile.activate + enrollment.update(status: :passed, proofed_at: proofed_at) analytics(user: enrollment.user).idv_in_person_usps_proofing_results_job_email_initiated( **email_analytics_attributes(enrollment), email_type: 'Success', ) - enrollment.profile.activate - enrollment.update(status: :passed) send_verified_email(enrollment.user, enrollment) end @@ -369,7 +378,7 @@ def send_verified_email(user, enrollment) # rubocop:disable IdentityIdp/MailLaterLinter UserMailer.with(user: user, email_address: email_address).in_person_verified( enrollment: enrollment, - ).deliver_later(**mail_delivery_params) + ).deliver_later(**mail_delivery_params(enrollment.proofed_at)) # rubocop:enable IdentityIdp/MailLaterLinter end end @@ -389,7 +398,7 @@ def send_failed_email(user, enrollment) # rubocop:disable IdentityIdp/MailLaterLinter UserMailer.with(user: user, email_address: email_address).in_person_failed( enrollment: enrollment, - ).deliver_later(**mail_delivery_params) + ).deliver_later(**mail_delivery_params(enrollment.proofed_at)) # rubocop:enable IdentityIdp/MailLaterLinter end end @@ -399,18 +408,26 @@ def send_failed_fraud_email(user, enrollment) # rubocop:disable IdentityIdp/MailLaterLinter UserMailer.with(user: user, email_address: email_address).in_person_failed_fraud( enrollment: enrollment, - ).deliver_later(**mail_delivery_params) + ).deliver_later(**mail_delivery_params(enrollment.proofed_at)) # rubocop:enable IdentityIdp/MailLaterLinter end end - def mail_delivery_params - config_delay = IdentityConfig.store.in_person_results_delay_in_hours - if config_delay > 0 - return { wait: config_delay.hours, queue: :intentionally_delayed } - elsif (config_delay == 0) - return {} - end - { wait: DEFAULT_EMAIL_DELAY_IN_HOURS.hours, queue: :intentionally_delayed } + def mail_delivery_params(proofed_at) + mail_delay_hours = IdentityConfig.store.in_person_results_delay_in_hours || + DEFAULT_EMAIL_DELAY_IN_HOURS + wait_until = proofed_at + mail_delay_hours.hours + return {} if mail_delay_hours == 0 || wait_until < Time.zone.now + return { wait_until: wait_until, queue: :intentionally_delayed } + end + + def parse_usps_timestamp(usps_timestamp) + return unless usps_timestamp + # Parse timestamps eg 12/17/2020 033855 => Thu, 17 Dec 2020 03:38:55 -0600 + # Note that the USPS timestamps are in Central Standard time (UTC -6:00) + ActiveSupport::TimeZone[-6].strptime( + usps_timestamp, + '%m/%d/%Y %H%M%S', + ).in_time_zone('UTC') end end diff --git a/db/primary_migrate/20230206184647_add_proofed_at_to_in_person_enrollments.rb b/db/primary_migrate/20230206184647_add_proofed_at_to_in_person_enrollments.rb new file mode 100644 index 00000000000..10ece4e35f2 --- /dev/null +++ b/db/primary_migrate/20230206184647_add_proofed_at_to_in_person_enrollments.rb @@ -0,0 +1,5 @@ +class AddProofedAtToInPersonEnrollments < ActiveRecord::Migration[7.0] + def change + add_column :in_person_enrollments, :proofed_at, :timestamp, comment: 'timestamp when user attempted to proof at a Post Office' + end +end diff --git a/db/schema.rb b/db/schema.rb index cfcbb9c0f27..8e240675cd5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -297,6 +297,7 @@ t.boolean "early_reminder_sent", default: false, comment: "early reminder to complete IPP before deadline sent" t.boolean "late_reminder_sent", default: false, comment: "late reminder to complete IPP before deadline sent" t.boolean "deadline_passed_sent", default: false, comment: "deadline passed email sent for expired enrollment" + t.datetime "proofed_at", precision: nil, comment: "timestamp when user attempted to proof at a Post Office" t.index ["profile_id"], name: "index_in_person_enrollments_on_profile_id" t.index ["status_check_attempted_at"], name: "index_in_person_enrollments_on_status_check_attempted_at", where: "(status = 1)" t.index ["unique_id"], name: "index_in_person_enrollments_on_unique_id", unique: true diff --git a/spec/factories/in_person_enrollments.rb b/spec/factories/in_person_enrollments.rb index 66280291d55..11c0a829c22 100644 --- a/spec/factories/in_person_enrollments.rb +++ b/spec/factories/in_person_enrollments.rb @@ -28,6 +28,7 @@ trait :failed do enrollment_code { Faker::Number.number(digits: 16) } enrollment_established_at { Time.zone.now } + proofed_at { Time.zone.now } status { :failed } status_check_attempted_at { Time.zone.now } status_updated_at { Time.zone.now } diff --git a/spec/jobs/get_usps_proofing_results_job_spec.rb b/spec/jobs/get_usps_proofing_results_job_spec.rb index bf5558be7ac..a39addc7f90 100644 --- a/spec/jobs/get_usps_proofing_results_job_spec.rb +++ b/spec/jobs/get_usps_proofing_results_job_spec.rb @@ -36,8 +36,8 @@ scan_count: response['scanCount'], secondary_id_type: response['secondaryIdType'], status: response['status'], - transaction_end_date_time: response['transactionEndDateTime'], - transaction_start_date_time: response['transactionStartDateTime'], + transaction_end_date_time: anything, + transaction_start_date_time: anything, ) end @@ -45,13 +45,14 @@ before(:each) do stub_request_passed_proofing_results end + it 'logs message with email analytics attributes' do freeze_time do job.perform(Time.zone.now) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Success or failure email initiated', email_type: anything, - delay_time_seconds: 3600, + wait_until: anything, service_provider: pending_enrollment.issuer, timestamp: Time.zone.now, user_id: pending_enrollment.user_id, @@ -144,6 +145,18 @@ let(:request_delay_ms) { 0 } let(:job) { GetUspsProofingResultsJob.new } let(:job_analytics) { FakeAnalytics.new } + let(:transaction_start_date_time) do + ActiveSupport::TimeZone['Central Time (US & Canada)'].strptime( + '12/17/2020 033855', + '%m/%d/%Y %H%M%S', + ).in_time_zone('UTC') + end + let(:transaction_end_date_time) do + ActiveSupport::TimeZone['Central Time (US & Canada)'].strptime( + '12/17/2020 034055', + '%m/%d/%Y %H%M%S', + ).in_time_zone('UTC') + end before do allow(Rails).to receive(:cache).and_return( @@ -342,7 +355,7 @@ end.to have_enqueued_mail(UserMailer, :in_person_failed).with( params: { user: user, email_address: user.email_addresses.first }, args: [{ enrollment: pending_enrollment }], - ).at(Time.zone.now + 1.hour).on_queue(:intentionally_delayed) + ) end end @@ -378,7 +391,7 @@ end.to have_enqueued_mail(UserMailer, :in_person_failed_fraud).with( params: { user: user, email_address: user.email_addresses.first }, args: [{ enrollment: pending_enrollment }], - ).at(Time.zone.now + 1.hour).on_queue(:intentionally_delayed) + ) end end @@ -393,26 +406,68 @@ end.to have_enqueued_mail(UserMailer, :in_person_verified).with( params: { user: user, email_address: user.email_addresses.first }, args: [{ enrollment: pending_enrollment }], - ).at(Time.zone.now + 1.hour).on_queue(:intentionally_delayed) + ) end end context 'a custom delay greater than zero is set' do - it 'uses the custom delay' do - stub_request_passed_proofing_results + let(:user) { pending_enrollment.user } + let(:proofed_at_string) do + proofed_at = ActiveSupport::TimeZone['Central Time (US & Canada)'].now - 1.hour + proofed_at.strftime('%m/%d/%Y %H%M%S') + end + before do allow(IdentityConfig.store). to(receive(:in_person_results_delay_in_hours).and_return(5)) - user = pending_enrollment.user + end + + it 'uses the custom delay when proofing passes' do + stub_request_passed_proofing_results(transactionEndDateTime: proofed_at_string) + wait_until = nil freeze_time do + wait_until = Time.zone.now + 4.hours expect do job.perform(Time.zone.now) end.to have_enqueued_mail(UserMailer, :in_person_verified).with( params: { user: user, email_address: user.email_addresses.first }, args: [{ enrollment: pending_enrollment }], - ).at(Time.zone.now + 5.hours).on_queue(:intentionally_delayed) + ).at(wait_until).on_queue(:intentionally_delayed) + end + + expect(job_analytics).to have_logged_event( + 'GetUspsProofingResultsJob: Success or failure email initiated', + email_type: 'Success', + service_provider: anything, + timestamp: anything, + user_id: anything, + wait_until: wait_until, + ) + end + + it 'uses the custom delay when proofing fails' do + stub_request_failed_proofing_results(transactionEndDateTime: proofed_at_string) + wait_until = nil + + freeze_time do + wait_until = Time.zone.now + 4.hours + expect do + job.perform(Time.zone.now) + end.to have_enqueued_mail(UserMailer, :in_person_failed).with( + params: { user: user, email_address: user.email_addresses.first }, + args: [{ enrollment: pending_enrollment }], + ).at(wait_until).on_queue(:intentionally_delayed) end + + expect(job_analytics).to have_logged_event( + 'GetUspsProofingResultsJob: Success or failure email initiated', + email_type: 'Failed', + service_provider: anything, + timestamp: anything, + user_id: anything, + wait_until: wait_until, + ) end end @@ -452,6 +507,7 @@ it 'logs details about the success' do job.perform(Time.zone.now) + expect(pending_enrollment.proofed_at).to eq(transaction_end_date_time) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Enrollment status updated', hash_including( @@ -461,11 +517,19 @@ ) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Success or failure email initiated', - delay_time_seconds: 3600, email_type: 'Success', service_provider: anything, timestamp: anything, user_id: anything, + wait_until: nil, + ) + + expect(job_analytics).to have_logged_event( + 'GetUspsProofingResultsJob: Enrollment status updated', + hash_including( + transaction_end_date_time: transaction_end_date_time, + transaction_start_date_time: transaction_start_date_time, + ), ) end end @@ -486,6 +550,7 @@ it 'logs failure details' do job.perform(Time.zone.now) + expect(pending_enrollment.proofed_at).to eq(transaction_end_date_time) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Enrollment status updated', hash_including( @@ -494,11 +559,19 @@ ) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Success or failure email initiated', - delay_time_seconds: 3600, email_type: 'Failed', service_provider: anything, timestamp: anything, user_id: anything, + wait_until: nil, + ) + + expect(job_analytics).to have_logged_event( + 'GetUspsProofingResultsJob: Enrollment status updated', + hash_including( + transaction_end_date_time: transaction_end_date_time, + transaction_start_date_time: transaction_start_date_time, + ), ) end end @@ -519,6 +592,7 @@ it 'logs fraud failure details' do job.perform(Time.zone.now) + expect(pending_enrollment.proofed_at).to eq(transaction_end_date_time) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Enrollment status updated', hash_including( @@ -527,11 +601,19 @@ ) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Success or failure email initiated', - delay_time_seconds: 3600, email_type: 'Failed fraud suspected', service_provider: anything, timestamp: anything, user_id: anything, + wait_until: nil, + ) + + expect(job_analytics).to have_logged_event( + 'GetUspsProofingResultsJob: Enrollment status updated', + hash_including( + transaction_end_date_time: transaction_end_date_time, + transaction_start_date_time: transaction_start_date_time, + ), ) end end @@ -552,13 +634,25 @@ it 'logs a message about the unsupported ID' do job.perform Time.zone.now + expect(pending_enrollment.proofed_at).to eq(transaction_end_date_time) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Enrollment status updated', hash_including( minutes_since_established: range_approximating(3.days.in_minutes, vary_right: 5), reason: 'Unsupported ID type', + transaction_end_date_time: transaction_end_date_time, + transaction_start_date_time: transaction_start_date_time, ), ) + + expect(job_analytics).to have_logged_event( + 'GetUspsProofingResultsJob: Success or failure email initiated', + email_type: 'Failed unsupported ID type', + service_provider: anything, + timestamp: anything, + user_id: anything, + wait_until: nil, + ) end end @@ -578,11 +672,14 @@ it 'logs that the enrollment expired' do job.perform(Time.zone.now) + expect(pending_enrollment.proofed_at).to eq(nil) expect(job_analytics).to have_logged_event( 'GetUspsProofingResultsJob: Enrollment status updated', hash_including( minutes_since_established: range_approximating(3.days.in_minutes, vary_right: 5), reason: 'Enrollment has expired', + transaction_end_date_time: nil, + transaction_start_date_time: nil, ), ) end diff --git a/spec/support/usps_ipp_helper.rb b/spec/support/usps_ipp_helper.rb index 939b6e71753..a8d15a1dbad 100644 --- a/spec/support/usps_ipp_helper.rb +++ b/spec/support/usps_ipp_helper.rb @@ -143,10 +143,13 @@ def request_unexpected_invalid_enrollment_code_args } end - def stub_request_failed_proofing_results - stub_request(:post, %r{/ivs-ippaas-api/IPPRest/resources/rest/getProofingResults}).to_return( - **request_failed_proofing_results_args, - ) + def stub_request_failed_proofing_results(overrides = {}) + response = merge_into_response_body(request_failed_proofing_results_args, overrides) + + stub_request( + :post, + %r{/ivs-ippaas-api/IPPRest/resources/rest/getProofingResults}, + ).to_return(response) end def request_failed_proofing_results_args @@ -190,10 +193,13 @@ def stub_request_passed_proofing_unsupported_status_results ) end - def stub_request_passed_proofing_results - stub_request(:post, %r{/ivs-ippaas-api/IPPRest/resources/rest/getProofingResults}).to_return( - **request_passed_proofing_results_args, - ) + def stub_request_passed_proofing_results(overrides = {}) + response = merge_into_response_body(request_passed_proofing_results_args, overrides) + + stub_request( + :post, + %r{/ivs-ippaas-api/IPPRest/resources/rest/getProofingResults}, + ).to_return(response) end def request_passed_proofing_results_args @@ -274,4 +280,19 @@ def stub_request_enrollment_code_with_forbidden_error %r{/ivs-ippaas-api/IPPRest/resources/rest/requestEnrollmentCode}, ).to_raise(Faraday::ForbiddenError) end + + private + + # Merges an object into the JSON string of a response's body and returns the updated response + def merge_into_response_body(response, body_overrides) + { + **response, + body: JSON.generate( + { + **JSON.parse(response[:body]), + **body_overrides, + }, + ), + } + end end From 4607236ebf449e38c12b01d09ee13f22195d01f5 Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 1 Mar 2023 12:38:45 -0600 Subject: [PATCH 02/12] Skip redundant delete and save of service provider authorization requests (#7910) * Skip redundant delete and save of service provider authorization requests changelog: Internal, Service Provider Authorization Requests, Skip redundant delete and save of authorization requests * Update app/services/service_provider_request_handler.rb Co-authored-by: Zach Margolis --------- Co-authored-by: Zach Margolis --- app/services/service_provider_request_handler.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/services/service_provider_request_handler.rb b/app/services/service_provider_request_handler.rb index 6ff66b0bf02..d23aecbf837 100644 --- a/app/services/service_provider_request_handler.rb +++ b/app/services/service_provider_request_handler.rb @@ -7,6 +7,8 @@ def initialize(url:, session:, protocol_request:, protocol:) end def call + return if sp_url_stored_in_session == url + pull_request_id_from_current_sp_session_id delete_sp_request_if_session_has_matching_request_id @@ -34,8 +36,17 @@ def current_sp end def sp_stored_in_session + service_provider_request_proxy&.issuer + end + + def sp_url_stored_in_session + service_provider_request_proxy&.url + end + + def service_provider_request_proxy return if sp_request_id.blank? - ServiceProviderRequestProxy.from_uuid(sp_request_id).issuer + return @service_provider_request_proxy if defined?(@service_provider_request_proxy) + @service_provider_request_proxy = ServiceProviderRequestProxy.from_uuid(sp_request_id) end def pull_request_id_from_current_sp_session_id From fbcdc999efe79e6383d9ad576fbb741ccd36e56b Mon Sep 17 00:00:00 2001 From: Matt Hinz Date: Wed, 1 Mar 2023 11:34:46 -0800 Subject: [PATCH 03/12] Handle address line 2 coming from Acuant (#7906) * Read Address Line 2 from Acuant responses changelog: User-Facing Improvements, Identity verification, Ensure that second line of address is read from identity documents * Log address_line2_present to Acuant * Guard against nil .Fields on PII (Was breaking a test) * Update DocumentProofingJob spec * Refactor fixture twiddling to use .tap * Add `Fields` to some acuant tests Changes to `GetResultsResponse` mean that we now attempt to read `Fields` on initialization, so we need `Fields` in our test data. --- app/services/doc_auth/acuant/pii_from_doc.rb | 1 + .../acuant/responses/get_results_response.rb | 2 ++ .../get_results_response_success.json | 34 ++++++++++++++++++- spec/jobs/document_proofing_job_spec.rb | 2 ++ .../doc_auth/acuant/acuant_client_spec.rb | 24 +++++++++++++ .../doc_auth/acuant/pii_from_doc_spec.rb | 1 + .../responses/get_results_response_spec.rb | 6 +++- 7 files changed, 68 insertions(+), 2 deletions(-) diff --git a/app/services/doc_auth/acuant/pii_from_doc.rb b/app/services/doc_auth/acuant/pii_from_doc.rb index 5772baacbe2..60c08afd91c 100644 --- a/app/services/doc_auth/acuant/pii_from_doc.rb +++ b/app/services/doc_auth/acuant/pii_from_doc.rb @@ -6,6 +6,7 @@ class PiiFromDoc 'Middle Name' => :middle_name, 'Surname' => :last_name, 'Address Line 1' => :address1, + 'Address Line 2' => :address2, 'Address City' => :city, 'Address State' => :state, 'Address Postal Code' => :zipcode, diff --git a/app/services/doc_auth/acuant/responses/get_results_response.rb b/app/services/doc_auth/acuant/responses/get_results_response.rb index 98acd8015a1..85812c608b9 100644 --- a/app/services/doc_auth/acuant/responses/get_results_response.rb +++ b/app/services/doc_auth/acuant/responses/get_results_response.rb @@ -54,6 +54,7 @@ def create_response_info alerts = processed_alerts log_alert_formatter = DocAuth::ProcessedAlertToLogAlertFormatter.new + { vendor: 'Acuant', billed: result_code.billed, @@ -64,6 +65,7 @@ def create_response_info image_metrics: processed_image_metrics, tamper_result: tamper_result_code&.name, classification_info: classification_info, + address_line2_present: !pii_from_doc[:address2].blank?, } end diff --git a/spec/fixtures/acuant_responses/get_results_response_success.json b/spec/fixtures/acuant_responses/get_results_response_success.json index 46c3f7faec9..cf4b5774265 100644 --- a/spec/fixtures/acuant_responses/get_results_response_success.json +++ b/spec/fixtures/acuant_responses/get_results_response_success.json @@ -605,6 +605,24 @@ "Type": "string", "Value": "1000 E AVENUE E" }, + { + "DataSource": 2, + "Description": "Second address line of the person's legal, residence, or mailing address", + "Id": "645d1c0a-eac9-4d21-a308-430205a7a49a", + "IsImage": false, + "Key": "2D Address Line 2", + "Name": "Address Line 2", + "RegionOfInterest": { + "height": 0, + "width": 0, + "x": 0, + "y": 0 + }, + "RegionReference": "00000000-0000-0000-0000-000000000000", + "Reliability": 0.98, + "Type": "string", + "Value": "APT E" + }, { "DataSource": 2, "Description": "Postal code of the person's address", @@ -1171,7 +1189,7 @@ "Name": "Address", "RegionReference": "00000000-0000-0000-0000-000000000000", "Type": "string", - "Value": "1000 E AVENUE EBISMARCK, ND 58501" + "Value": "1000 E AVENUE E APT E BISMARCK, ND 58501" }, { "DataFieldReferences": [ @@ -1201,6 +1219,20 @@ "Type": "string", "Value": "1000 E AVENUE E" }, + { + "DataFieldReferences": [ + "645d1c0a-eac9-4d21-a308-430205a7a49a" + ], + "DataSource": 2, + "Description": "Second address line of the person's legal, residence, or mailing address", + "Id": "f541c1cc-9f98-4131-a92b-0049b6126acd", + "IsImage": false, + "Key": "Address Line 2", + "Name": "Address Line 2", + "RegionReference": "00000000-0000-0000-0000-000000000000", + "Type": "string", + "Value": "APT E" + }, { "DataFieldReferences": [ "208d17a8-6056-4ccf-b89d-b904c27e50c1" diff --git a/spec/jobs/document_proofing_job_spec.rb b/spec/jobs/document_proofing_job_spec.rb index 81b309ffbd8..e458eb28876 100644 --- a/spec/jobs/document_proofing_job_spec.rb +++ b/spec/jobs/document_proofing_job_spec.rb @@ -124,6 +124,7 @@ exception: nil, tamper_result: nil, classification_info: nil, + address_line2_present: false, ) expect(job_analytics).to have_logged_event( @@ -151,6 +152,7 @@ flow_path: 'standard', log_alert_results: {}, classification_info: nil, + address_line2_present: false, ) expect(result.pii_from_doc).to eq(applicant_pii) diff --git a/spec/services/doc_auth/acuant/acuant_client_spec.rb b/spec/services/doc_auth/acuant/acuant_client_spec.rb index c9ca7de8ed5..ea74ae32ced 100644 --- a/spec/services/doc_auth/acuant/acuant_client_spec.rb +++ b/spec/services/doc_auth/acuant/acuant_client_spec.rb @@ -144,6 +144,30 @@ expect(result.success?).to eq(true) end + it 'notes that address line 2 was present' do + instance_id = 'this-is-a-test-instance-id' + url = URI.join(assure_id_url, "/AssureIDService/Document/#{instance_id}") + stub_request(:get, url).to_return(body: AcuantFixtures.get_results_response_success) + + result = subject.get_results(instance_id: instance_id) + + expect(result.extra).to include(address_line2_present: true) + end + + context 'when there is no address line 2' do + it 'notes that address line 2 was not present' do + instance_id = 'this-is-a-test-instance-id' + url = URI.join(assure_id_url, "/AssureIDService/Document/#{instance_id}") + body = JSON.parse(AcuantFixtures.get_results_response_success).tap do |json| + json['Fields'] = json['Fields'].select { |f| f['Name'] != 'Address Line 2' } + end.to_json + stub_request(:get, url).to_return(body: body) + + result = subject.get_results(instance_id: instance_id) + + expect(result.extra).to include(address_line2_present: false) + end + end end context 'when the result is a failure' do diff --git a/spec/services/doc_auth/acuant/pii_from_doc_spec.rb b/spec/services/doc_auth/acuant/pii_from_doc_spec.rb index bbe8094c0ea..92780fe2004 100644 --- a/spec/services/doc_auth/acuant/pii_from_doc_spec.rb +++ b/spec/services/doc_auth/acuant/pii_from_doc_spec.rb @@ -12,6 +12,7 @@ middle_name: nil, last_name: 'DOE', address1: '1000 E AVENUE E', + address2: 'APT E', city: 'BISMARCK', state: 'ND', zipcode: '58501', diff --git a/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb b/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb index 1aa9a7e5ba2..eb33f01fb01 100644 --- a/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb +++ b/spec/services/doc_auth/acuant/responses/get_results_response_spec.rb @@ -56,6 +56,7 @@ 'IssuerName' => 'North Dakota', }, }, + address_line2_present: true, } processed_alerts = response_hash[:processed_alerts] @@ -80,6 +81,7 @@ middle_name: nil, last_name: 'DOE', address1: '1000 E AVENUE E', + address2: 'APT E', city: 'BISMARCK', state: 'ND', zipcode: '58501', @@ -100,6 +102,7 @@ body: { Result: 5, Alerts: alerts, + Fields: [], }.to_json, ) end @@ -269,6 +272,7 @@ { Result: 1, Key: 'Birth Date Valid' }, { Result: 2, Key: 'Document Classification' }, ], + Fields: [], }.to_json, ) end @@ -315,7 +319,7 @@ let(:http_response) do instance_double( Faraday::Response, - body: { Result: result, Alerts: alerts }.to_json, + body: { Result: result, Alerts: alerts, Fields: [] }.to_json, ) end From e675ebc0cba2df5dae2dc6ab33e6162b54100632 Mon Sep 17 00:00:00 2001 From: gina-yamada <125507397+gina-yamada@users.noreply.github.com> Date: Wed, 1 Mar 2023 12:42:49 -0700 Subject: [PATCH 04/12] Gyamada/lg 8861 usps locations are sorted (#7900) * LG-8861: Sort facilities distance in asc order * LG-8861: Create unordered distance mock data * LG-8861: Test to sort unordered facilities * changelog: User-Facing Improvements, In-person proofing, Sorts facilities by ascending distance (users address to post office) * LG-8861: Fix lint issues * LG-8861 Remove dups bef sort, use each_cons in test * LG-8861 fix spelling error --- .../usps_in_person_proofing/mock/fixtures.rb | 4 + ...ties_response_with_unordered_distance.json | 203 ++++++++++++++++++ .../usps_in_person_proofing/proofer.rb | 7 +- .../usps_in_person_proofing/proofer_spec.rb | 10 + spec/support/usps_ipp_helper.rb | 9 + 5 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 app/services/usps_in_person_proofing/mock/responses/request_facilities_response_with_unordered_distance.json diff --git a/app/services/usps_in_person_proofing/mock/fixtures.rb b/app/services/usps_in_person_proofing/mock/fixtures.rb index 45f7191dd86..8a5b48ad896 100644 --- a/app/services/usps_in_person_proofing/mock/fixtures.rb +++ b/app/services/usps_in_person_proofing/mock/fixtures.rb @@ -17,6 +17,10 @@ def self.request_facilities_response load_response_fixture('request_facilities_response.json') end + def self.request_facilities_response_with_unordered_distance + load_response_fixture('request_facilities_response_with_unordered_distance.json') + end + def self.request_facilities_response_with_duplicates load_response_fixture('request_facilities_response_with_duplicates.json') end diff --git a/app/services/usps_in_person_proofing/mock/responses/request_facilities_response_with_unordered_distance.json b/app/services/usps_in_person_proofing/mock/responses/request_facilities_response_with_unordered_distance.json new file mode 100644 index 00000000000..d90ec9bae3f --- /dev/null +++ b/app/services/usps_in_person_proofing/mock/responses/request_facilities_response_with_unordered_distance.json @@ -0,0 +1,203 @@ +{ + "postOffices": [ + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "6.63 mi", + "streetAddress": "4750 J ST", + "city": "SACRAMENTO", + "phone": "916-227-6503", + "name": "CAMELLIA", + "zip4": "9998", + "state": "CA", + "zip5": "95819" + }, + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "3.87 mi", + "streetAddress": "660 J ST STE 170", + "city": "SACRAMENTO", + "phone": "916-498-9145", + "name": "DOWNTOWN PLAZA", + "zip4": "9996", + "state": "CA", + "zip5": "95814" + }, + { + "parking": "Street", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "0.05 mi", + "streetAddress": "3775 INDUSTRIAL BLVD", + "city": "WEST SACRAMENTO", + "phone": "916-556-3406", + "name": "INDUSTRIAL WEST SACRAMENTO", + "zip4": "9998", + "state": "CA", + "zip5": "95799" + }, + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "4.53 mi", + "streetAddress": "2121 BROADWAY", + "city": "SACRAMENTO", + "name": "BROADWAY", + "phone": "916-227-6503", + "zip4": "9998", + "state": "CA", + "zip5": "95818" + }, + { + "parking": "Street", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "2.77 mi", + "streetAddress": "900 SACRAMENTO AVE", + "city": "WEST SACRAMENTO", + "phone": "916-556-3406", + "name": "BRODERICK", + "zip4": "9998", + "state": "CA", + "zip5": "95605" + }, + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "5.33 mi", + "streetAddress": "1618 ALHAMBRA BLVD", + "city": "SACRAMENTO", + "phone": "916-227-6503", + "name": "FORT SUTTER", + "zip4": "9998", + "state": "CA", + "zip5": "95816" + }, + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "4.55 mi", + "streetAddress": "5930 S LAND PARK DR", + "city": "SACRAMENTO", + "phone": "916-262-3107", + "name": "LAND PARK", + "zip4": "9998", + "state": "CA", + "zip5": "95822" + }, + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 6:00 PM" + }, + { + "saturdayHours": "9:00 AM - 3:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "5.49 mi", + "streetAddress": "2929 35TH ST", + "city": "SACRAMENTO", + "phone": "916-227-6509", + "name": "OAK PARK", + "zip4": "9998", + "state": "CA", + "zip5": "95817" + }, + { + "parking": "Lot", + "hours": [ + { + "weekdayHours": "9:00 AM - 5:00 PM" + }, + { + "saturdayHours": "9:00 AM - 5:00 PM" + }, + { + "sundayHours": "Closed" + } + ], + "distance": "2.22 mi", + "streetAddress": "1601 MERKLEY AVE", + "city": "WEST SACRAMENTO", + "name": "WEST SACRAMENTO", + "phone": "916-556-3406", + "state": "CA", + "zip4": "9998", + "zip5": "95691" + } + ] + } + \ No newline at end of file diff --git a/app/services/usps_in_person_proofing/proofer.rb b/app/services/usps_in_person_proofing/proofer.rb index 9825ae2676d..79dafbce50e 100644 --- a/app/services/usps_in_person_proofing/proofer.rb +++ b/app/services/usps_in_person_proofing/proofer.rb @@ -24,7 +24,8 @@ def request_facilities(location) end.body facilities = parse_facilities(response) - dedupe_facilities(facilities) + dedupe_facilities = dedupe_facilities(facilities) + sort_by_ascending_distance(dedupe_facilities) end # Temporary function to return a static set of facilities @@ -230,5 +231,9 @@ def dedupe_facilities(facilities) [facility.address, facility.city, facility.state, facility.zip_code_5] end end + + def sort_by_ascending_distance(facilities) + facilities.sort_by { |f| f[:distance].to_f } + end end end diff --git a/spec/services/usps_in_person_proofing/proofer_spec.rb b/spec/services/usps_in_person_proofing/proofer_spec.rb index 77451b38d03..bbdd5483695 100644 --- a/spec/services/usps_in_person_proofing/proofer_spec.rb +++ b/spec/services/usps_in_person_proofing/proofer_spec.rb @@ -161,6 +161,16 @@ def check_for_token_refresh_and_method_call(cache, redis) check_facility(facilities[0]) end + it 'returns facilities sorted by ascending distance' do + stub_request_facilities_with_unordered_distance + facilities = subject.request_facilities(location) + + expect(facilities.count).to be > 1 + facilities.each_cons(2) do |facility_a, facility_b| + expect(facility_a.distance).to be <= facility_b.distance + end + end + it 'does not return duplicates' do stub_request_facilities_with_duplicates facilities = subject.request_facilities(location) diff --git a/spec/support/usps_ipp_helper.rb b/spec/support/usps_ipp_helper.rb index a8d15a1dbad..56256960f98 100644 --- a/spec/support/usps_ipp_helper.rb +++ b/spec/support/usps_ipp_helper.rb @@ -23,6 +23,15 @@ def stub_request_facilities ) end + def stub_request_facilities_with_unordered_distance + stub_request(:post, %r{/ivs-ippaas-api/IPPRest/resources/rest/getIppFacilityList}).to_return( + status: 200, + body: + UspsInPersonProofing::Mock::Fixtures.request_facilities_response_with_unordered_distance, + headers: { 'content-type' => 'application/json' }, + ) + end + def stub_request_facilities_with_duplicates stub_request(:post, %r{/ivs-ippaas-api/IPPRest/resources/rest/getIppFacilityList}).to_return( status: 200, From 09b799f3ec75e21cfd6a3a88f9b558a0a2c21794 Mon Sep 17 00:00:00 2001 From: Sonia Connolly Date: Wed, 1 Mar 2023 13:20:56 -0800 Subject: [PATCH 05/12] Verify info spec refactor (#7911) * Add GOOD_SSN_MASKED constant And take out some indirection in verify_info_step_spec feature spec. [skip changelog] --------- Co-authored-by: Luis H. Matos --- .../idv/doc_auth/verify_info_step_spec.rb | 225 +++++++++--------- spec/support/features/doc_auth_helper.rb | 1 + 2 files changed, 109 insertions(+), 117 deletions(-) diff --git a/spec/features/idv/doc_auth/verify_info_step_spec.rb b/spec/features/idv/doc_auth/verify_info_step_spec.rb index de6815cdee9..d4df7d5ca15 100644 --- a/spec/features/idv/doc_auth/verify_info_step_spec.rb +++ b/spec/features/idv/doc_auth/verify_info_step_spec.rb @@ -7,12 +7,7 @@ let(:fake_analytics) { FakeAnalytics.new } let(:fake_attempts_tracker) { IrsAttemptsApiTrackingHelper::FakeAttemptsTracker.new } - let(:mock_ssn_a) { DocAuthHelper::GOOD_SSN } - let(:masked_ssn_a) { '9**-**-***4' } - let(:mock_zip_code) { '12345' } - let(:mock_ssn_b) { '900456789' } - let(:masked_ssn_b) { '9**-**-***9' } - let(:unmasked_ssn_b) { '900-45-6789' } + # values from Idp::Constants::MOCK_IDV_APPLICANT let(:fake_pii_details) do { document_state: 'MT', @@ -25,23 +20,6 @@ address: '1 FAKE RD', } end - let(:mock_state_id_jurisdiction) { [Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction]] } - let(:proof_resolution_args) do - { - trace_id: anything, - threatmetrix_session_id: anything, - request_ip: kind_of(String), - } - end - - let(:forms_ssn_show) { 'forms.ssn.show' } - let(:forms_buttons_submit_update) { 'forms.buttons.submit.update' } - let(:idv_buttons_change_ssn_label) { 'idv.buttons.change_ssn_label' } - let(:idv_form_ssn_label_html) { 'idv.form.ssn_label_html' } - let(:idv_failure_button_warning) { 'idv.failure.button.warning' } - let(:step_verify_info_controller) { 'Idv::VerifyInfoController' } - let(:ananlyics_throttle_event) { 'Throttler Rate Limit Triggered' } - let(:idv_failure_timeout) { 'idv.failure.timeout' } before do allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(fake_analytics) @@ -65,33 +43,33 @@ expect(page).to have_content(t('step_indicator.flows.idv.verify_info')) # SSN is masked until revealed - expect(page).to have_text(masked_ssn_a) - expect(page).not_to have_text(mock_ssn_a) - check t(forms_ssn_show) - expect(page).not_to have_text(masked_ssn_a) - expect(page).to have_text(mock_ssn_a) + expect(page).to have_text(DocAuthHelper::GOOD_SSN_MASKED) + expect(page).not_to have_text(DocAuthHelper::GOOD_SSN) + check t('forms.ssn.show') + expect(page).not_to have_text(DocAuthHelper::GOOD_SSN_MASKED) + expect(page).to have_text(DocAuthHelper::GOOD_SSN) end it 'allows the user to enter in a new address and displays updated info' do click_button t('idv.buttons.change_address_label') - fill_in 'idv_form_zipcode', with: mock_zip_code - click_button t(forms_buttons_submit_update) + fill_in 'idv_form_zipcode', with: '12345' + click_button t('forms.buttons.submit.update') expect(page).to have_current_path(idv_verify_info_path) - expect(page).to have_content(mock_zip_code) + expect(page).to have_content('12345') end it 'allows the user to enter in a new ssn and displays updated info' do - click_button t(idv_buttons_change_ssn_label) - fill_in t(idv_form_ssn_label_html), with: mock_ssn_b - click_button t(forms_buttons_submit_update) + click_button t('idv.buttons.change_ssn_label') + fill_in t('idv.form.ssn_label_html'), with: '900456789' + click_button t('forms.buttons.submit.update') expect(page).to have_current_path(idv_verify_info_path) - expect(page).to have_text(masked_ssn_b) - check t(forms_ssn_show) - expect(page).to have_text(unmasked_ssn_b) + expect(page).to have_text('9**-**-***9') + check t('forms.ssn.show') + expect(page).to have_text('900-45-6789') end it 'proceeds to the next page upon confirmation' do @@ -99,7 +77,7 @@ success: true, failure_reason: nil, **fake_pii_details, - ssn: mock_ssn_a, + ssn: DocAuthHelper::GOOD_SSN, ) sign_in_and_2fa_user complete_doc_auth_steps_before_verify_step @@ -131,7 +109,7 @@ click_idv_continue expect(page).to have_current_path(idv_session_errors_warning_path) - click_on t(idv_failure_button_warning) + click_on t('idv.failure.button.warning') expect(page).to have_current_path(idv_verify_info_path) end @@ -152,12 +130,12 @@ expect(fake_analytics).to have_logged_event( 'IdV: doc auth exception visited', - step_name: step_verify_info_controller, + step_name: 'Idv::VerifyInfoController', remaining_attempts: 5, ) expect(page).to have_current_path(idv_session_errors_exception_path) - click_on t(idv_failure_button_warning) + click_on t('idv.failure.button.warning') expect(page).to have_current_path(idv_verify_info_path) end @@ -191,9 +169,9 @@ click_idv_continue expect(page).to have_current_path(idv_session_errors_failure_path) expect(fake_analytics).to have_logged_event( - ananlyics_throttle_event, + 'Throttler Rate Limit Triggered', throttle_type: :idv_resolution, - step_name: step_verify_info_controller, + step_name: 'Idv::VerifyInfoController', ) visit idv_verify_info_url @@ -237,7 +215,7 @@ click_idv_continue expect(page).to have_current_path(idv_session_errors_ssn_failure_path) expect(fake_analytics).to have_logged_event( - ananlyics_throttle_event, + 'Throttler Rate Limit Triggered', throttle_type: :proof_ssn, step_name: 'verify_info', ) @@ -255,76 +233,89 @@ end end - context 'when the user lives in an AAMVA supported state' do - it 'performs a resolution and state ID check' do - allow(IdentityConfig.store).to receive(:aamva_supported_jurisdictions).and_return( - mock_state_id_jurisdiction, - ) - user = create(:user, :signed_up) - expect_any_instance_of(Idv::Agent). - to receive(:proof_resolution). - with( - anything, - should_proof_state_id: true, - user_id: user.id, - **proof_resolution_args, - ). - and_call_original - - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_verify_step - click_idv_continue - - expect(DocAuthLog.find_by(user_id: user.id).aamva).not_to be_nil + context 'AAMVA' do + let(:mock_state_id_jurisdiction) do + [Idp::Constants::MOCK_IDV_APPLICANT[:state_id_jurisdiction]] + end + let(:proof_resolution_args) do + { + trace_id: anything, + threatmetrix_session_id: anything, + request_ip: kind_of(String), + } end - end - context 'when the user does not live in an AAMVA supported state' do - it 'does not perform the state ID check' do - allow(IdentityConfig.store).to receive(:aamva_supported_jurisdictions).and_return( - IdentityConfig.store.aamva_supported_jurisdictions - + context 'when the user lives in an AAMVA supported state' do + it 'performs a resolution and state ID check' do + allow(IdentityConfig.store).to receive(:aamva_supported_jurisdictions).and_return( mock_state_id_jurisdiction, - ) - user = create(:user, :signed_up) - expect_any_instance_of(Idv::Agent). - to receive(:proof_resolution). - with( - anything, - should_proof_state_id: false, - user_id: user.id, - **proof_resolution_args, - ). - and_call_original - - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_verify_step - click_idv_continue + ) + user = create(:user, :signed_up) + expect_any_instance_of(Idv::Agent). + to receive(:proof_resolution). + with( + anything, + should_proof_state_id: true, + user_id: user.id, + **proof_resolution_args, + ). + and_call_original + + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_verify_step + click_idv_continue - expect(DocAuthLog.find_by(user_id: user.id).aamva).to be_nil + expect(DocAuthLog.find_by(user_id: user.id).aamva).not_to be_nil + end end - end - context 'when the SP is in the AAMVA banlist' do - it 'does not perform the state ID check' do - allow(IdentityConfig.store).to receive(:aamva_sp_banlist_issuers). - and_return('["urn:gov:gsa:openidconnect:sp:server"]') - user = create(:user, :signed_up) - expect_any_instance_of(Idv::Agent). - to receive(:proof_resolution). - with( - anything, - should_proof_state_id: false, - user_id: user.id, - **proof_resolution_args, - ). - and_call_original - - visit_idp_from_sp_with_ial1(:oidc) - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_verify_step - click_idv_continue + context 'when the user does not live in an AAMVA supported state' do + it 'does not perform the state ID check' do + allow(IdentityConfig.store).to receive(:aamva_supported_jurisdictions).and_return( + IdentityConfig.store.aamva_supported_jurisdictions - + mock_state_id_jurisdiction, + ) + user = create(:user, :signed_up) + expect_any_instance_of(Idv::Agent). + to receive(:proof_resolution). + with( + anything, + should_proof_state_id: false, + user_id: user.id, + **proof_resolution_args, + ). + and_call_original + + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_verify_step + click_idv_continue - expect(DocAuthLog.find_by(user_id: user.id).aamva).to be_nil + expect(DocAuthLog.find_by(user_id: user.id).aamva).to be_nil + end + end + + context 'when the SP is in the AAMVA banlist' do + it 'does not perform the state ID check' do + allow(IdentityConfig.store).to receive(:aamva_sp_banlist_issuers). + and_return('["urn:gov:gsa:openidconnect:sp:server"]') + user = create(:user, :signed_up) + expect_any_instance_of(Idv::Agent). + to receive(:proof_resolution). + with( + anything, + should_proof_state_id: false, + user_id: user.id, + **proof_resolution_args, + ). + and_call_original + + visit_idp_from_sp_with_ial1(:oidc) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_verify_step + click_idv_continue + + expect(DocAuthLog.find_by(user_id: user.id).aamva).to be_nil + end end end @@ -338,7 +329,7 @@ click_idv_continue expect(fake_analytics).to have_logged_event('Proofing Resolution Result Missing') - expect(page).to have_content(t(idv_failure_timeout)) + expect(page).to have_content(t('idv.failure.timeout')) expect(page).to have_current_path(idv_verify_info_path) allow(DocumentCaptureSession).to receive(:find_by).and_call_original click_idv_continue @@ -350,7 +341,7 @@ success: false, failure_reason: { idv_verification: [:timeout] }, **fake_pii_details, - ssn: mock_ssn_a, + ssn: DocAuthHelper::GOOD_SSN, ) sign_in_and_2fa_user complete_doc_auth_steps_before_verify_step @@ -359,7 +350,7 @@ and_return(nil) click_idv_continue - expect(page).to have_content(t(idv_failure_timeout)) + expect(page).to have_content(t('idv.failure.timeout')) expect(page).to have_current_path(idv_verify_info_path) allow(DocumentCaptureSession).to receive(:find_by).and_call_original end @@ -374,7 +365,7 @@ and_return(nil) click_idv_continue - expect(page).to have_content(t(idv_failure_timeout)) + expect(page).to have_content(t('idv.failure.timeout')) expect(page).to have_current_path(idv_verify_info_path) allow(DocumentCaptureSession).to receive(:find_by).and_call_original click_idv_continue @@ -391,11 +382,11 @@ end it 'uses ssn controller to enter a new ssn and displays updated info' do - click_link t(idv_buttons_change_ssn_label) + click_link t('idv.buttons.change_ssn_label') expect(page).to have_current_path(idv_ssn_path) - fill_in t(idv_form_ssn_label_html), with: mock_ssn_b - click_button t(forms_buttons_submit_update) + fill_in t('idv.form.ssn_label_html'), with: '900456789' + click_button t('forms.buttons.submit.update') expect(fake_analytics).to have_logged_event( 'IdV: doc auth redo_ssn submitted', @@ -403,9 +394,9 @@ expect(page).to have_current_path(idv_verify_info_path) - expect(page).to have_text(masked_ssn_b) - check t(forms_ssn_show) - expect(page).to have_text(unmasked_ssn_b) + expect(page).to have_text('9**-**-***9') + check t('forms.ssn.show') + expect(page).to have_text('900-45-6789') end end end diff --git a/spec/support/features/doc_auth_helper.rb b/spec/support/features/doc_auth_helper.rb index 18d042afcd4..26850771f31 100644 --- a/spec/support/features/doc_auth_helper.rb +++ b/spec/support/features/doc_auth_helper.rb @@ -4,6 +4,7 @@ module DocAuthHelper include DocumentCaptureStepHelper GOOD_SSN = Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN[:ssn] + GOOD_SSN_MASKED = '9**-**-***4' SSN_THAT_FAILS_RESOLUTION = '123-45-6666' SSN_THAT_RAISES_EXCEPTION = '000-00-0000' From cc63a9f38f53d33efe794eaf2e5bc1f3e3d39352 Mon Sep 17 00:00:00 2001 From: Kimball Bighorse Date: Wed, 1 Mar 2023 16:19:56 -0700 Subject: [PATCH 06/12] Change link sent page (#7807) * Move and modify alert banner * Capture formatted phone in send link step and return it in form response * Store phone in idv session instead of form response * [skip changelog] * Pass phone value for view * Store raw phone and display in link sent step * Hyphenate * Revert phone extrapolation * Remove empty line * Return correct value * Add translations * Size image element and remove enclosing p element around message * Increase bottom margin of alert * Use correct margin bottom class * Use correct Spanish character * Delete phone.png * Pushing failing test (do not merge) * Fix formatting * Replace phone icon in upload form * Consolidate multi-line calls * Updating spec and test helpers to check for combined upload flag * Updating combined upload step feature flag context test -- What This is relevant to the link_sent step tests * Store phone in session for upload step * Fix style * Store phone in session outside of phone getters --------- Co-authored-by: eric-gade --- app/assets/images/idv/phone.png | Bin 1956 -> 0 bytes app/services/idv/steps/link_sent_step.rb | 4 + app/services/idv/steps/send_link_step.rb | 1 + app/services/idv/steps/upload_step.rb | 1 + app/views/idv/doc_auth/_upload.html.erb | 6 +- app/views/idv/doc_auth/link_sent.html.erb | 24 ++- config/locales/doc_auth/en.yml | 6 +- config/locales/doc_auth/es.yml | 1 + config/locales/doc_auth/fr.yml | 1 + .../idv/doc_auth/link_sent_step_spec.rb | 185 ++++++++++-------- spec/support/features/doc_auth_helper.rb | 10 +- 11 files changed, 141 insertions(+), 98 deletions(-) delete mode 100644 app/assets/images/idv/phone.png diff --git a/app/assets/images/idv/phone.png b/app/assets/images/idv/phone.png deleted file mode 100644 index a5080a5369391ab53058e85074c9d43f07252211..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1956 zcmV;V2V3}wP){r z6a=4$5$6Y*SYuHeSo$|OKn8)Y@RRQADq&j=9ll| z+;jQkTtE3?Lu9IizHBU+<^NRp$qyUIoX56IdVpE7%aTRLqD)NO17i^*Jr;Z6C*Q%2 z+t1uJi+!?tU{XL~GKW8bBdB12(k=SZ1Xl2a#2p-Xlk;IhqnqN81&k|cD z6BEvsmN3`Es=s2I-sSb7qVqEIh(}a;yV^P8lGpG$018z|2$-b z-+diu$z>Yf1^{#C6|QZT*t<(&6&UGcEO-OE%m)X}SkMTD6)?%*uTanS{=Z~00>G}o zqERqJLRs!I8lGcSp9-C8L|c(0G|H0AGuUPR66y@^L_LYnDbCkL zkB(2;ekAvV@_L=g)aNtU)z}81bJa+V4QZ%)y*PJ#eC$sFK|%19gyh`7knk1jfZe9i zVvKzULx>W0gHwO!QLC?dBmdy!h)91zoWpX#qNS*CwB9X^9Ghxr!Kn$K(JI>C1?zLOC9gWFOO z8Xgt1B{g&3fy0OX%FQ`)+?g=ko0XHiEhB8%T6o(So0o*GEGR7Y4#u)%k+|!~nvaa> zye^Dwoys)6<^(GWJ`GzsZ|J0fOCmNUZ!fu6)~?k#9L%t_5r%fVL({G;D!vrADJ5W0 z_}H#q;Te0fN4Pyiog!0?%sHrgGv7WRHozXQN#!ReEC>o3THE-f)VlhH9xHRP#gtjD zg}l&gGBLw%umnMXE98ca$zxe(_^yPc?ViAxmmI}#hrl#HV6y>xDgOZ1jBwFAUve3= zIo0}UX^L0YUV@yOR(-Ha=gExDoIM{Jxn?YjynbV{M=-)%J?hQiHNU}qtQTYJXf@QWyR=CrhGC^$@fr`wTLsXJ0@&|5ed!tU?Sgso!OKJu& z;CuTG(QyfNC%|$A{{!$2-nl!=X0t;BzZ!iX7>4@?z`H86<2GLTEV=UBon9DPEY@V= z!2N--XcTQ{h0bVLy}W=oZP}sLw3n;RGcs$2g1^bADQoS6iCT^9F=gkeTisnY9)_^p z+YrBnK=5FQ80nBDPtZp&&F^~z!_~F$+y$exUo|tc=C$ZcP}^@aYW}V@m|5h$Q0Jd6 z3<+OFEO;QWAaS*PN1#VAtoyN>Qj|C9zqeNB7B5+FjLO8 ze(eOi-;2g3Y&~04qHb%4J%;#TglI74TltP4SiL-fU3G@u zhD&@}=KekV5ADgyaV89RWbBJiObv-xJy=_!!C34;#uTRU&7q4HA@>H40gPGER=~DQ z0O!*HmL$HO)BFg?rvi*o7wTTmwtUF8PT<;R@Y>nDE*N!2mzS(3MDnOBEHM&WD>}*~ zrc&aRP;Jbsu+ULid65vwsKCC*wdz_zBos`+6imSsOu-aPd{Qt4Q!oXSA)#OjreF%D zU<#&S;*)|Yn1U&o3<(8OFa=XE1ye8u6Q2}J!4yovWJoBOf+?7SDVTyOnE0e%3Z`HR zCPPBO6imSsOu-aP!Ney8Q!oWnFc}gGreF%DU<#&SSy6i(4vQoN<27BUP>u?G^VaP@ zmaQekASK<1#Z)5lTy+N@%VI9Yf@w-t6AqzDN1IB|$*Y+pq=(E{P<>Id~mfwrWH1g;IuLd_5H3_mCvX;b0n@ zTHs{zxXs%_BUaDO0QI!%@!!eXrHL%oh=9gG`WLe><-71 qNC>DX%kjK(i5eOFlm!L<-{W6Wjk8{nnrjIF0000
<%= image_tag( - asset_url('idv/phone.png'), + asset_url('idv/phone-icon.svg'), alt: t('image_description.camera_mobile_phone'), - width: 80, - height: 119, + width: 88, + height: 88, ) %>
diff --git a/app/views/idv/doc_auth/link_sent.html.erb b/app/views/idv/doc_auth/link_sent.html.erb index d37c9185006..64a557bacae 100644 --- a/app/views/idv/doc_auth/link_sent.html.erb +++ b/app/views/idv/doc_auth/link_sent.html.erb @@ -11,22 +11,26 @@ message: flow_session[:error_message], ) %> <% end %> -<%= render PageHeadingComponent.new.with_content(t('doc_auth.headings.text_message')) %> +<%= render AlertComponent.new(type: :warning, class: 'margin-bottom-4') do %> + <%= t('doc_auth.info.keep_window_open') %> + <% if FeatureManagement.doc_capture_polling_enabled? %> + <%= t('doc_auth.info.link_sent_complete_polling') %> + <% else %> + <%= t('doc_auth.info.link_sent_complete_no_polling') %> + <% end %> +<% end %> +<%= render PageHeadingComponent.new.with_content(t('doc_auth.headings.text_message')) %>
- <%= image_tag asset_url('idv/phone.png'), width: 80, height: 119, alt: t('image_description.camera_mobile_phone') %> + <%= image_tag asset_url('idv/phone-icon.svg'), width: 88, height: 88, alt: t('image_description.camera_mobile_phone') %>
+

+ <%= t('doc_auth.info.you_entered') %> + <%= local_assigns[:phone] %> +

<%= t('doc_auth.info.link_sent') %>

- <%= render AlertComponent.new(type: :warning) do %> - <%= t('doc_auth.info.keep_window_open') %> - <% if FeatureManagement.doc_capture_polling_enabled? %> - <%= t('doc_auth.info.link_sent_complete_polling') %> - <% else %> - <%= t('doc_auth.info.link_sent_complete_no_polling') %> - <% end %> - <% end %>
diff --git a/config/locales/doc_auth/en.yml b/config/locales/doc_auth/en.yml index f1e39120a50..0b1f16f6464 100644 --- a/config/locales/doc_auth/en.yml +++ b/config/locales/doc_auth/en.yml @@ -167,10 +167,9 @@ en: keep_window_open: Do not close this window. lets_go: 'Identity verification happens in two parts:' link_sent: Please check your phone and follow instructions to take a photo of - your state issued ID. + your state-issued ID. link_sent_complete_no_polling: When you are done, click Continue here to finish verifying your identity. - link_sent_complete_polling: The next step will load automatically once you - verify your ID using your phone. + link_sent_complete_polling: The next step will load automatically. no_sp_name: The agency that you are trying to access privacy: '%{app_name} is a secure, government website that adheres to the highest standards in data protection. We use your data to verify your @@ -193,6 +192,7 @@ en: against public records. welcome_html: '%{sp_name} needs to make sure you are you — not someone pretending to be you.' + you_entered: 'You entered:' instructions: bullet1: State-issued ID bullet2: Social Security number diff --git a/config/locales/doc_auth/es.yml b/config/locales/doc_auth/es.yml index 03558395608..81d05bfbcd6 100644 --- a/config/locales/doc_auth/es.yml +++ b/config/locales/doc_auth/es.yml @@ -228,6 +228,7 @@ es: identidad comparándola con los registros públicos. welcome_html: '%{sp_name} necesita asegurarse de que es usted y no es alguien que se hace pasar por usted.' + you_entered: 'Ud. entregó:' instructions: bullet1: Documento de identidad emitido por el estado. bullet2: Número de seguro social diff --git a/config/locales/doc_auth/fr.yml b/config/locales/doc_auth/fr.yml index d0060e17fba..440ada355b8 100644 --- a/config/locales/doc_auth/fr.yml +++ b/config/locales/doc_auth/fr.yml @@ -237,6 +237,7 @@ fr: vérifier votre identité par rapport aux registres publics. welcome_html: '%{sp_name} doit s’assurer que vous êtes bien vous, et non quelqu’un qui se fait passer pour vous.' + you_entered: 'Tu as soumis:' instructions: bullet1: Carte d’identité délivrée par l’État bullet2: Numéro de sécurité sociale diff --git a/spec/features/idv/doc_auth/link_sent_step_spec.rb b/spec/features/idv/doc_auth/link_sent_step_spec.rb index 4d23bbe5bf9..e3d5281e940 100644 --- a/spec/features/idv/doc_auth/link_sent_step_spec.rb +++ b/spec/features/idv/doc_auth/link_sent_step_spec.rb @@ -5,115 +5,140 @@ include DocAuthHelper include DocCaptureHelper - let(:user) { sign_in_and_2fa_user } - let(:doc_capture_polling_enabled) { false } - - before do - allow(FeatureManagement).to receive(:doc_capture_polling_enabled?). - and_return(doc_capture_polling_enabled) - user - complete_doc_auth_steps_before_link_sent_step - end + context 'with combined upload step disabled' do + let(:user) { sign_in_and_2fa_user } + let(:doc_capture_polling_enabled) { false } - it 'proceeds to the next page with valid info' do - expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) + before do + allow(FeatureManagement).to receive(:doc_capture_polling_enabled?). + and_return(doc_capture_polling_enabled) + user + complete_doc_auth_steps_before_link_sent_step + end - mock_doc_captured(user.id) - click_idv_continue + it 'proceeds to the next page with valid info' do + expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_id')) - expect(page).to have_current_path(idv_doc_auth_ssn_step) - end + mock_doc_captured(user.id) + click_idv_continue - it 'proceeds to the next page if the user does not have a phone' do - user = create(:user, :with_authentication_app, :with_piv_or_cac) - sign_in_and_2fa_user(user) - complete_doc_auth_steps_before_link_sent_step - mock_doc_captured(user.id) - click_idv_continue + expect(page).to have_current_path(idv_doc_auth_ssn_step) + end - expect(page).to have_current_path(idv_doc_auth_ssn_step) - end + it 'proceeds to the next page if the user does not have a phone' do + user = create(:user, :with_authentication_app, :with_piv_or_cac) + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_link_sent_step + mock_doc_captured(user.id) + click_idv_continue - it 'does not proceed to the next page if the capture flow is incomplete' do - click_idv_continue + expect(page).to have_current_path(idv_doc_auth_ssn_step) + end - expect(page).to have_current_path(idv_doc_auth_link_sent_step) - end + it 'does not proceed to the next page if the capture flow is incomplete' do + click_idv_continue - it 'does not proceed to the next page if the capture flow is unsuccessful' do - mock_doc_captured(user.id, DocAuth::Response.new(success: false)) + expect(page).to have_current_path(idv_doc_auth_link_sent_step) + end - click_idv_continue + it 'does not proceed to the next page if the capture flow is unsuccessful' do + mock_doc_captured(user.id, DocAuth::Response.new(success: false)) - expect(page).to have_current_path(idv_doc_auth_link_sent_step) - end + click_idv_continue - context 'cancelled' do - before do - document_capture_session = user.document_capture_sessions.last - document_capture_session.cancelled_at = Time.zone.now - document_capture_session.save! + expect(page).to have_current_path(idv_doc_auth_link_sent_step) end - it 'redirects to before hybrid flow started and shows alert text' do - click_idv_continue + context 'cancelled' do + before do + document_capture_session = user.document_capture_sessions.last + document_capture_session.cancelled_at = Time.zone.now + document_capture_session.save! + end - expect(page).to have_current_path(idv_doc_auth_upload_step) - expect(page).to have_css( - '.usa-alert--error', - text: t('errors.doc_auth.document_capture_cancelled'), - ) + it 'redirects to before hybrid flow started and shows alert text' do + click_idv_continue + + expect(page).to have_current_path(idv_doc_auth_upload_step) + expect(page).to have_css( + '.usa-alert--error', + text: t('errors.doc_auth.document_capture_cancelled'), + ) + end end - end - shared_examples 'with doc capture polling enabled' do - metadata[:js] = true - let(:doc_capture_polling_enabled) { true } + shared_examples 'with doc capture polling enabled' do + metadata[:js] = true + let(:doc_capture_polling_enabled) { true } - it 'automatically advances when the mobile flow is complete' do - expect(page).to_not have_css 'meta[http-equiv="refresh"]', visible: false - expect(page).to_not have_button(t('forms.buttons.continue')) - expect(page).to_not have_content(t('doc_auth.info.link_sent_complete_no_polling')) - expect(page).to have_content(t('doc_auth.info.link_sent_complete_polling')) + it 'automatically advances when the mobile flow is complete' do + expect(page).to_not have_css 'meta[http-equiv="refresh"]', visible: false + expect(page).to_not have_button(t('forms.buttons.continue')) + expect(page).to_not have_content(t('doc_auth.info.link_sent_complete_no_polling')) + expect(page).to have_content(t('doc_auth.info.link_sent_complete_polling')) - mock_doc_captured(user.id) + mock_doc_captured(user.id) - expect(page).to have_content(t('doc_auth.headings.ssn'), wait: 6) - expect(page).to have_current_path(idv_doc_auth_ssn_step) + expect(page).to have_content(t('doc_auth.headings.ssn'), wait: 6) + expect(page).to have_current_path(idv_doc_auth_ssn_step) + end end - end - shared_examples 'with doc capture polling disabled' do - let(:doc_capture_polling_enabled) { false } + shared_examples 'with doc capture polling disabled' do + let(:doc_capture_polling_enabled) { false } - context 'clicks back link' do - before do - click_doc_auth_back_link + context 'clicks back link' do + before do + click_doc_auth_back_link - visit idv_doc_auth_link_sent_step - end + visit idv_doc_auth_link_sent_step + end - it 'redirects to send link step' do - expect(page).to have_current_path(idv_doc_auth_send_link_step) + it 'redirects to send link step' do + expect(page).to have_current_path(idv_doc_auth_send_link_step) + end end - end - it 'refreshes page 4x with meta refresh extending timeout by 40 min and can start over' do - 4.times do + it 'refreshes page 4x with meta refresh extending timeout by 40 min and can start over' do + 4.times do + expect(page).to have_css 'meta[http-equiv="refresh"]', visible: false + visit idv_doc_auth_link_sent_step + end + expect(page).to_not have_css 'meta[http-equiv="refresh"]', visible: false + + click_doc_auth_back_link + click_doc_auth_back_link + click_link t('links.cancel') + click_on t('idv.cancel.actions.start_over') + complete_doc_auth_steps_before_link_sent_step expect(page).to have_css 'meta[http-equiv="refresh"]', visible: false - visit idv_doc_auth_link_sent_step end - expect(page).to_not have_css 'meta[http-equiv="refresh"]', visible: false - - click_doc_auth_back_link - click_doc_auth_back_link - click_link t('links.cancel') - click_on t('idv.cancel.actions.start_over') - complete_doc_auth_steps_before_link_sent_step - expect(page).to have_css 'meta[http-equiv="refresh"]', visible: false end + + it_behaves_like 'with doc capture polling enabled' + it_behaves_like 'with doc capture polling disabled' end - it_behaves_like 'with doc capture polling enabled' - it_behaves_like 'with doc capture polling disabled' + context 'with combined upload step enabled', js: true do + let(:user) { sign_in_and_2fa_user } + let(:doc_capture_polling_enabled) { false } + let(:phone_number) { '415-555-0199' } + + before do + allow(IdentityConfig.store). + to(receive(:doc_auth_combined_hybrid_handoff_enabled).and_return(true)) + allow(FeatureManagement). + to(receive(:doc_capture_polling_enabled?).and_return(doc_capture_polling_enabled)) + user + complete_doc_auth_steps_before_upload_step + fill_in :doc_auth_phone, with: '' + fill_in :doc_auth_phone, with: phone_number + click_send_link + end + + it 'Correctly renders the link sent step page' do + expect(page).to have_current_path(idv_doc_auth_link_sent_step) + expect(page).to have_content(phone_number) + end + end end diff --git a/spec/support/features/doc_auth_helper.rb b/spec/support/features/doc_auth_helper.rb index 26850771f31..cb3ec0ddc96 100644 --- a/spec/support/features/doc_auth_helper.rb +++ b/spec/support/features/doc_auth_helper.rb @@ -168,12 +168,18 @@ def complete_doc_auth_steps_before_address_step(expect_accessible: false) def complete_doc_auth_steps_before_send_link_step complete_doc_auth_steps_before_upload_step - click_on t('doc_auth.buttons.use_phone') + if IdentityConfig.store.doc_auth_combined_hybrid_handoff_enabled + click_on t('forms.buttons.send_link') + else + click_on t('doc_auth.buttons.use_phone') + end end def complete_doc_auth_steps_before_link_sent_step complete_doc_auth_steps_before_send_link_step - fill_out_doc_auth_phone_form_ok + if !IdentityConfig.store.doc_auth_combined_hybrid_handoff_enabled + fill_out_doc_auth_phone_form_ok + end click_idv_continue end From ce5387a30d0410387081991a5d3319170ded887f Mon Sep 17 00:00:00 2001 From: Malick Diarra Date: Thu, 2 Mar 2023 11:02:37 -0500 Subject: [PATCH 07/12] LG-8553: Don't submit without nickname for security keys (#7867) * changelog: Bug Fixes, Authentication, don't allow users to submit without nickname for security key * rubocop * fix spec for webauthn * update javascript to be on form submit * erb lint fix * fix up javascript * on javascript submit the form without prompting for javascript * fix indentation --- .../users/webauthn_setup_controller.rb | 13 ++++--------- app/javascript/packs/webauthn-setup.ts | 18 +++--------------- app/views/users/webauthn_setup/new.html.erb | 11 +++++------ spec/features/webauthn/management_spec.rb | 4 ++-- spec/features/webauthn/sign_up_spec.rb | 2 +- spec/support/features/webauthn_helper.rb | 6 +++--- 6 files changed, 18 insertions(+), 36 deletions(-) diff --git a/app/controllers/users/webauthn_setup_controller.rb b/app/controllers/users/webauthn_setup_controller.rb index b2d1346f14f..2b04aa2dd38 100644 --- a/app/controllers/users/webauthn_setup_controller.rb +++ b/app/controllers/users/webauthn_setup_controller.rb @@ -183,17 +183,12 @@ def process_invalid_webauthn(form) else flash.now[:error] = t('errors.webauthn_setup.unique_name') end - - render :new + elsif form.platform_authenticator? + flash[:error] = t('errors.webauthn_platform_setup.general_error') else - if form.platform_authenticator? - flash[:error] = t('errors.webauthn_platform_setup.general_error') - else - flash[:error] = t('errors.webauthn_setup.general_error') - end - - redirect_to account_two_factor_authentication_path + flash[:error] = t('errors.webauthn_setup.general_error') end + render :new end def mark_user_as_fully_authenticated diff --git a/app/javascript/packs/webauthn-setup.ts b/app/javascript/packs/webauthn-setup.ts index f0b68e2eda2..3915be22c44 100644 --- a/app/javascript/packs/webauthn-setup.ts +++ b/app/javascript/packs/webauthn-setup.ts @@ -19,8 +19,9 @@ function webauthn() { if (!isWebAuthnEnabled()) { reloadWithError('NotSupportedError'); } - const continueButton = document.getElementById('continue-button')!; - continueButton.addEventListener('click', () => { + const form = document.getElementById('webauthn_form') as HTMLFormElement; + form.addEventListener('submit', (event) => { + event.preventDefault(); document.getElementById('spinner')!.classList.remove('display-none'); document.getElementById('continue-button')!.className = 'display-none'; @@ -47,19 +48,6 @@ function webauthn() { }) .catch((err) => reloadWithError(err.name, { force: true })); }); - const input = document.getElementById('nickname') as HTMLInputElement; - input.addEventListener('keypress', function (event) { - if (event.keyCode === 13) { - // prevent form submit - event.preventDefault(); - } - }); - input.addEventListener('keyup', function (event) { - event.preventDefault(); - if (event.keyCode === 13 && input.value) { - continueButton.click(); - } - }); } if (process.env.NODE_ENV !== 'test') { diff --git a/app/views/users/webauthn_setup/new.html.erb b/app/views/users/webauthn_setup/new.html.erb index e5b741968b1..611731e5386 100644 --- a/app/views/users/webauthn_setup/new.html.erb +++ b/app/views/users/webauthn_setup/new.html.erb @@ -52,13 +52,12 @@ checked: @presenter.remember_device_box_checked?, }, ) %> - <%= submit_tag t('forms.buttons.submit.default'), id: 'submit-button', class: 'display-none' %> + <%= submit_tag( + @presenter.button_text, + id: 'continue-button', + class: 'display-block usa-button usa-button--big usa-button--wide margin-y-5', + ) %> <% end %> -<%= button_tag( - @presenter.button_text, - class: 'display-block usa-button usa-button--big usa-button--wide margin-y-5', - id: 'continue-button', - ) %>