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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions app/services/doc_auth/classification_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ def id_type_supported?

alias_method :doc_type_supported?, :id_type_supported?

private
private

# @param [Object] classification_info assureid classification info
# @param [String] doc_side value of ['Front', 'Back']
def doc_side_class_ok?(classification_info, doc_side)
side_type = classification_info&.with_indifferent_access&.dig(doc_side, :ClassName)
!side_type.present? ||
DocAuth::Response::ID_TYPE_SLUGS.key?(side_type) ||
(
IdentityConfig.store.doc_auth_passports_enabled ?
DocAuth::Response::ID_TYPE_SLUGS.key?(side_type) :
DocAuth::Response::STATE_ID_TYPE_SLUGS.key?(side_type)
) ||
side_type == 'Unknown'
end

Expand All @@ -50,5 +54,5 @@ def doc_issuer_type_ok?(classification_info, doc_side)
def supported_country_codes
IdentityConfig.store.doc_auth_supported_country_codes
end
end
end
end
4 changes: 3 additions & 1 deletion app/services/doc_auth/lexis_nexis/doc_pii_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ def read_pii(true_id_product)
return nil unless id_auth_field_data.present?

id_doc_type_slug = id_auth_field_data['Fields_DocumentClassName']
@id_doc_type = DocAuth::Response::ID_TYPE_SLUGS[id_doc_type_slug]
@id_doc_type = IdentityConfig.store.doc_auth_passports_enabled ?
DocAuth::Response::ID_TYPE_SLUGS[id_doc_type_slug] :
DocAuth::Response::STATE_ID_TYPE_SLUGS[id_doc_type_slug]

if id_doc_type == 'drivers_license' || id_doc_type == 'state_id_card'
generate_state_id_pii
Expand Down
3 changes: 2 additions & 1 deletion app/services/doc_auth/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ class Response
ID_TYPE_SLUGS = {
'Identification Card' => 'state_id_card',
'Drivers License' => 'drivers_license',
'Passport' => 'passport',
}.freeze
Comment on lines 11 to 15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe define ID_TYPE_SLUGS after STATE_ID_TYPE_SLUGS so we eliminate the repeated strings?

Suggested change
ID_TYPE_SLUGS = {
'Identification Card' => 'state_id_card',
'Drivers License' => 'drivers_license',
'Passport' => 'passport',
}.freeze
ID_TYPE_SLUGS = STATE_ID_TYPE_SLUGS.merge({
'Passport' => 'passport'
}.freeze


SOCURE_ID_TYPE_SLUGS = {
STATE_ID_TYPE_SLUGS = {
'Identification Card' => 'state_id_card',
'Drivers License' => 'drivers_license',
}.freeze
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def parse_date(date_string)
end

def id_type_supported?
DocAuth::Response::SOCURE_ID_TYPE_SLUGS.key?(document_id_type)
DocAuth::Response::STATE_ID_TYPE_SLUGS.key?(document_id_type)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't there be an update to the Lexis Nexis response. I feel like it should have a similar setup with id_type_supported? So that even if Lexis Nexis comes back with a passport we reject it. This leads me to think we need to ensure that we limit the supported types depending on which flow the user is in. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would most likely need to be new ticket

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the same

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the update to the LN response is handled here

end

def reason_codes_selfie_pass
Expand Down
18 changes: 18 additions & 0 deletions spec/controllers/idv/choose_id_type_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@
expect(response).to redirect_to(idv_document_capture_url)
end
end

context 'user selects passport' do
let(:chosen_id_type) { 'passport' }

it 'sets document_capture_session to passport requested' do
put :update, params: params

expect(subject.document_capture_session.passport_requested?).to eq(true)
end

# currently we do not have a passport route so it redirects to ipp route
# change when the new passport is added
it 'redirects to passport document capture' do
put :update, params: params

expect(response).to redirect_to(idv_document_capture_url)
end
end
end

describe '#step_info' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,18 @@
expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
end
end

context 'user chooses passport' do
let(:chosen_id_type) { 'passport' }
let(:params) do
{ doc_auth: { choose_id_type_preference: chosen_id_type } }
end

it 'sets passport_status to requested and redirects to vendor that supports passport' do
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Newline before it block

put :update, params: params
expect(document_capture_session.passport_status).to eq('requested')
expect(response).to redirect_to idv_hybrid_mobile_document_capture_url
end
end
end
end
43 changes: 43 additions & 0 deletions spec/controllers/idv/in_person/choose_id_type_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,49 @@
let!(:enrollment) { create(:in_person_enrollment, :establishing, user: user) }

context 'when the form submission is successful' do
context 'when the chosen ID type is "passport"' do
let(:chosen_id_type) { 'passport' }
let(:params) do
{
doc_auth: {
choose_id_type_preference: chosen_id_type,
},
}
end
let(:analytics_arguments) do
{
flow_path: 'standard',
step: 'choose_id_type',
analytics_id: 'In Person Proofing',
skip_hybrid_handoff: false,
opted_in_to_in_person_proofing: true,
chosen_id_type: chosen_id_type,
success: true,
}
end

before do
subject.idv_session.opted_in_to_in_person_proofing =
analytics_arguments[:opted_in_to_in_person_proofing]
subject.idv_session.skip_hybrid_handoff = analytics_arguments[:skip_hybrid_handoff]
put :update, params: params
end

it 'logs the idv_in_person_proofing_choose_id_type_submitted event' do
expect(@analytics).to have_logged_event(
:idv_in_person_proofing_choose_id_type_submitted, analytics_arguments
)
end

it 'updates the passport status to "requested" in document capture session' do
expect(controller.document_capture_session.passport_status).to eq('requested')
end

it 'redirects to the in person passport page' do
expect(response).to redirect_to(idv_in_person_passport_path)
end
end

context 'when the chosen ID type is "drivers_license"' do
let(:chosen_id_type) { 'drivers_license' }
let(:params) do
Expand Down
20 changes: 20 additions & 0 deletions spec/features/idv/doc_auth/choose_id_type_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@
reload_ab_tests
end

context 'desktop flow', :js do
before do
complete_doc_auth_steps_before_hybrid_handoff_step
end

it 'shows choose id type screen and continues after passport option' do
expect(page).to have_content(t('doc_auth.headings.upload_from_computer'))
click_on t('forms.buttons.upload_photos')
expect(page).to have_current_path(idv_choose_id_type_url)
choose(t('doc_auth.forms.id_type_preference.passport'))
click_on t('forms.buttons.continue')
expect(page).to have_current_path(idv_document_capture_url)
visit idv_choose_id_type_url
expect(page).to have_checked_field(
'doc_auth_choose_id_type_preference_passport',
visible: :all,
)
end
end

context 'mobile flow', :js, driver: :headless_chrome_mobile do
before do
allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
Expand Down
181 changes: 181 additions & 0 deletions spec/features/idv/doc_auth/document_capture_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,187 @@
end
end

context 'Passports enabled', allow_browser_log: true do
let(:passports_enabled) { true }
let(:api_status) { 'UP' }
let(:ipp_service_provider) do
create(:service_provider, :active, :in_person_proofing_enabled)
end

before do
allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled).and_return(true)
allow_any_instance_of(ServiceProvider).to receive(
:in_person_proofing_enabled,
).and_return(true)
allow(IdentityConfig.store).to receive(:doc_auth_passports_percent).and_return(100)
stub_request(:get, IdentityConfig.store.dos_passport_composite_healthcheck_endpoint)
.to_return({ status: 200, body: { status: api_status }.to_json })
reload_ab_tests

visit_idp_from_sp_with_ial2(
:oidc,
**{ client_id: ipp_service_provider.issuer },
)
sign_in_and_2fa_user(@user)
complete_doc_auth_steps_before_document_capture_step
end

after do
reload_ab_tests
end

context 'with a valid passport' do
let(:passport_image) do
Rails.root.join(
'spec', 'fixtures',
'passport_credential.yml'
)
end

it 'happy path' do
choose_id_type(:passport)
expect(page).to have_content(t('doc_auth.headings.document_capture_passport'))
expect(page).to have_current_path(idv_document_capture_url)

expect(page).not_to have_content(t('doc_auth.tips.document_capture_selfie_text1'))
attach_passport_image(passport_image)
submit_images
expect(page).to have_content(t('doc_auth.headings.capture_complete'))
fill_out_ssn_form_ok
click_idv_continue
expect_step_indicator_current_step(t('step_indicator.flows.idv.verify_info'))
expect(page).to have_content(t('doc_auth.headings.address'))
fill_in 'idv_form_address1', with: '123 Main St'
fill_in 'idv_form_city', with: 'Nowhere'
select 'Virginia', from: 'idv_form_state'
fill_in 'idv_form_zipcode', with: '66044'
click_idv_continue
expect(page).to have_current_path(idv_verify_info_path)
expect(page).to have_content('VA')
expect(page).to have_content('123 Main St')
expect(page).to have_content('Nowhere')
complete_verify_step
expect(page).to have_current_path(idv_phone_url)
end
end

context 'with an invalid passport' do
let(:passport_image) do
Rails.root.join(
'spec', 'fixtures',
'passport_bad_mrz_credential.yml'
)
end

it 'fails due to mrz' do
choose_id_type(:passport)
expect(page).to have_current_path(idv_document_capture_url)
expect(page).not_to have_content(t('doc_auth.tips.document_capture_selfie_text1'))
attach_passport_image(passport_image)
submit_images
expect(page).not_to have_content(t('doc_auth.headings.capture_complete'))
expect(page).to have_content(t('doc_auth.info.review_passport'))
expect(page).to have_content(t('in_person_proofing.headings.cta'))
expect_to_try_again
expect(page).to have_content(t('doc_auth.info.review_passport'))
expect_rate_limit_warning(max_attempts - 1)
end
end

context 'with a network error' do
let(:passport_image) do
Rails.root.join(
'spec', 'fixtures',
'passport_credential.yml'
)
end
before do
DocAuth::Mock::DocAuthMockClient.mock_response!(
method: :post_passport_image,
response: DocAuth::Response.new(
success: false,
errors: { network: I18n.t('doc_auth.errors.general.network_error') },
),
)
end

it 'shows the error message' do
choose_id_type(:passport)
expect(page).to have_current_path(idv_document_capture_url)
expect(page).not_to have_content(t('doc_auth.tips.document_capture_selfie_text1'))
attach_passport_image(passport_image)
submit_images
expect(page).to have_content(t('doc_auth.errors.general.network_error'))
expect(page).to have_content(t('in_person_proofing.headings.cta'))
expect_rate_limit_warning(max_attempts - 1)
end
end

context 'pii validation error' do
let(:passport_image) do
Rails.root.join(
'spec', 'fixtures',
'passport_bad_pii_credentials.yml'
)
end

it 'fails pii check' do
choose_id_type(:passport)
expect(page).to have_current_path(idv_document_capture_url)
expect(page).not_to have_content(t('doc_auth.tips.document_capture_selfie_text1'))
attach_passport_image(passport_image)
submit_images
expect(page).to have_content(t('in_person_proofing.headings.cta'))
expect_to_try_again
expect(page).to have_current_path(idv_document_capture_url)
expect_rate_limit_warning(max_attempts - 1)
end
end

context 'api 400 error' do
let(:fake_dos_api_endpoint) { 'http://fake_dos_api_endpoint/' }

before do
allow(IdentityConfig.store).to receive(:dos_passport_mrz_endpoint)
.and_return(fake_dos_api_endpoint)
stub_request(:post, fake_dos_api_endpoint)
.to_return(status: 400, body: '{}', headers: {})
end

it 'shows the error message' do
choose_id_type(:passport)
expect(page).to have_current_path(idv_document_capture_url)
expect(page).not_to have_content(t('doc_auth.tips.document_capture_selfie_text1'))
attach_passport_image
submit_images
expect(page).to have_content(t('doc_auth.headings.review_issues_passport'))
expect(page).to have_current_path(idv_document_capture_url)
end
end

context 'api 500 error' do
let(:fake_dos_api_endpoint) { 'http://fake_dos_api_endpoint/' }

before do
allow(IdentityConfig.store).to receive(:dos_passport_mrz_endpoint)
.and_return(fake_dos_api_endpoint)
stub_request(:post, fake_dos_api_endpoint)
.to_return(status: 500, body: '{}', headers: {})
end

it 'shows the error message' do
choose_id_type(:passport)
expect(page).to have_current_path(idv_document_capture_url)
expect(page).not_to have_content(t('doc_auth.tips.document_capture_selfie_text1'))
attach_passport_image
submit_images
expect(page).to have_content(t('doc_auth.headings.review_issues_passport'))
expect(page).to have_current_path(idv_document_capture_url)
end
end
end

context 'standard desktop flow' do
before do
visit_idp_from_oidc_sp_with_ial2
Expand Down
Loading