Skip to content
4 changes: 3 additions & 1 deletion app/controllers/concerns/idv/document_capture_concern.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module Idv
module DocumentCaptureConcern
def override_document_capture_step_csp
return if params[:step] != 'document_capture'
if !IdentityConfig.store.doc_auth_document_capture_controller_enabled
return if params[:step] != 'document_capture'
end

policy = current_content_security_policy
policy.connect_src(*policy.connect_src, 'us.acas.acuant.net')
Expand Down
10 changes: 10 additions & 0 deletions app/controllers/concerns/idv/step_utilities_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,15 @@ def irs_reproofing?
service_provider: current_sp,
).present?
end

def document_capture_session
@document_capture_session ||= DocumentCaptureSession.find_by(
uuid: flow_session[document_capture_session_uuid_key],
)
end

def document_capture_session_uuid_key
:document_capture_session_uuid
end
end
end
105 changes: 97 additions & 8 deletions app/controllers/idv/document_capture_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,35 @@ class DocumentCaptureController < ApplicationController
include IdvSession
include StepIndicatorConcern
include StepUtilitiesConcern
include DocumentCaptureConcern

before_action :render_404_if_document_capture_controller_disabled
before_action :confirm_two_factor_authenticated
before_action :confirm_agreement_step_complete
before_action :override_document_capture_step_csp

def show
increment_step_counts

analytics.idv_doc_auth_document_capture_visited(**analytics_arguments)

Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
call('document_capture', :view, true)

render :show, locals: extra_view_variables
end

def update
handle_stored_result

analytics.idv_doc_auth_document_capture_submitted(**analytics_arguments)

Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
call('document_capture', :update, true)

redirect_to idv_ssn_url
end

def extra_view_variables
url_builder = ImageUploadPresignedUrlGenerator.new

Expand All @@ -33,7 +50,6 @@ def extra_view_variables
transaction_id: flow_session[:document_capture_session_uuid],
),
}.merge(
native_camera_ab_testing_variables,
acuant_sdk_upgrade_a_b_testing_variables,
in_person_cta_variant_testing_variables,
)
Expand All @@ -45,6 +61,12 @@ def render_404_if_document_capture_controller_disabled
render_not_found unless IdentityConfig.store.doc_auth_document_capture_controller_enabled
end

def confirm_agreement_step_complete
return if flow_session['Idv::Steps::AgreementStep']

redirect_to idv_doc_auth_url
end

def analytics_arguments
{
flow_path: flow_path,
Expand All @@ -65,13 +87,6 @@ def increment_step_counts
current_flow_step_counts['Idv::Steps::DocumentCaptureStep'] += 1
end

def native_camera_ab_testing_variables
{
acuant_sdk_upgrade_ab_test_bucket:
AbTests::ACUANT_SDK.bucket(flow_session[:document_capture_session_uuid]),
}
end

def acuant_sdk_upgrade_a_b_testing_variables
bucket = AbTests::ACUANT_SDK.bucket(flow_session[:document_capture_session_uuid])
testing_enabled = IdentityConfig.store.idv_acuant_sdk_upgrade_a_b_testing_enabled
Expand All @@ -97,5 +112,79 @@ def in_person_cta_variant_testing_variables
in_person_cta_variant_active: bucket,
}
end

def handle_stored_result
if stored_result&.success?
save_proofing_components
extract_pii_from_doc(stored_result, store_in_session: !hybrid_flow_mobile?)
else
extra = { stored_result_present: stored_result.present? }
failure(I18n.t('doc_auth.errors.general.network_error'), extra)
end
end

def stored_result
return @stored_result if defined?(@stored_result)
@stored_result = document_capture_session&.load_result
end

def save_proofing_components
return unless current_user

doc_auth_vendor = DocAuthRouter.doc_auth_vendor(
discriminator: flow_session[document_capture_session_uuid_key],
analytics: analytics,
)

component_attributes = {
document_check: doc_auth_vendor,
document_type: 'state_id',
}
ProofingComponent.create_or_find_by(user: current_user).update(component_attributes)
end

def hybrid_flow_mobile?
user_id_from_token.present?
end

def user_id_from_token
flow_session[:doc_capture_user_id]
end

# copied from doc_auth_base_step.rb
# @param [DocAuth::Response,
# DocumentCaptureSessionAsyncResult,
# DocumentCaptureSessionResult] response
def extract_pii_from_doc(response, store_in_session: false)
pii_from_doc = response.pii_from_doc.merge(
uuid: effective_user.uuid,
phone: effective_user.phone_configurations.take&.phone,
uuid_prefix: ServiceProvider.find_by(issuer: sp_session[:issuer])&.app_id,
)

flow_session[:had_barcode_read_failure] = response.attention_with_barcode?
if store_in_session
flow_session[:pii_from_doc] ||= {}
flow_session[:pii_from_doc].merge!(pii_from_doc)
idv_session.clear_applicant!
end
track_document_state(pii_from_doc[:state])
end

def track_document_state(state)
return unless IdentityConfig.store.state_tracking_enabled && state
doc_auth_log = DocAuthLog.find_by(user_id: current_user.id)
return unless doc_auth_log
doc_auth_log.state = state
doc_auth_log.save!
end

# copied from Flow::Failure module
def failure(message, extra = nil)
flow_session[:error_message] = message
form_response_params = { success: false, errors: { message: message } }
form_response_params[:extra] = extra unless extra.nil?
FormResponse.new(**form_response_params)
end
end
end
6 changes: 5 additions & 1 deletion app/services/idv/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,16 @@ def phone_confirmed?

def invalidate_steps_after_ssn!
# Guard against unvalidated attributes from in-person flow in review controller
session[:applicant] = nil
clear_applicant!

invalidate_verify_info_step!
invalidate_phone_step!
end

def clear_applicant!
session[:applicant] = nil
end

def mark_verify_info_step_complete!
session[:resolution_successful] = true
# This is here to maintain backwards compadibility with old code.
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@
get '/forgot_password' => 'forgot_password#new'
post '/forgot_password' => 'forgot_password#update'
get '/document_capture' => 'document_capture#show'
put '/document_capture' => 'document_capture#update'
get '/ssn' => 'ssn#show'
put '/ssn' => 'ssn#update'
get '/verify_info' => 'verify_info#show'
Expand Down
89 changes: 74 additions & 15 deletions spec/controllers/idv/document_capture_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
{ 'document_capture_session_uuid' => 'fd14e181-6fb1-4cdc-92e0-ef66dad0df4e',
'pii_from_doc' => Idp::Constants::MOCK_IDV_APPLICANT.dup,
:threatmetrix_session_id => 'c90ae7a5-6629-4e77-b97c-f1987c2df7d0',
:flow_path => 'standard' }
:flow_path => 'standard',
'Idv::Steps::AgreementStep' => true }
end

let(:user) { build(:user) }
let(:user) { create(:user) }
let(:service_provider) do
create(
:service_provider,
Expand Down Expand Up @@ -41,6 +42,13 @@
:confirm_two_factor_authenticated,
)
end

it 'checks that agreement step is complete' do
expect(subject).to have_actions(
:before,
:confirm_agreement_step_complete,
)
end
end

context 'when doc_auth_document_capture_controller_enabled' do
Expand All @@ -64,28 +72,79 @@
}
end

context '#show' do
it 'renders the show template' do
get :show
it 'renders the show template' do
get :show

expect(response).to render_template :show
end
expect(response).to render_template :show
end

it 'sends analytics_visited event' do
get :show
it 'sends analytics_visited event' do
get :show

expect(@analytics).to have_received(:track_event).with(analytics_name, analytics_args)
end
expect(@analytics).to have_received(:track_event).with(analytics_name, analytics_args)
end

it 'sends correct step count to analytics' do
get :show
get :show
analytics_args[:step_count] = 2

expect(@analytics).to have_received(:track_event).with(analytics_name, analytics_args)
end

it 'updates DocAuthLog document_capture_view_count' do
doc_auth_log = DocAuthLog.create(user_id: user.id)

expect { get :show }.to(
change { doc_auth_log.reload.document_capture_view_count }.from(0).to(1),
)
end

context 'agreement step is not complete' do
it 'redirects to idv_doc_auth_url' do
flow_session['Idv::Steps::AgreementStep'] = nil

it 'sends correct step count to analytics' do
get :show
get :show
analytics_args[:step_count] = 2

expect(@analytics).to have_received(:track_event).with(analytics_name, analytics_args)
expect(response).to redirect_to(idv_doc_auth_url)
end
end
end

describe '#update' do
let(:analytics_name) { 'IdV: doc auth document_capture submitted' }
let(:analytics_args) do
{
analytics_id: 'Doc Auth',
flow_path: 'standard',
irs_reproofing: false,
step: 'document capture',
step_count: 1,
}
end

it 'does not raise an exception when stored_result is nil' do
allow(FeatureManagement).to receive(:document_capture_async_uploads_enabled?).
and_return(false)
allow(subject).to receive(:stored_result).and_return(nil)
put :update
end

it 'sends analytics_submitted event with correct step count' do
get :show
put :update

expect(@analytics).to have_received(:track_event).with(analytics_name, analytics_args)
end

it 'updates DocAuthLog document_capture_submit_count' do
doc_auth_log = DocAuthLog.create(user_id: user.id)

expect { put :update }.to(
change { doc_auth_log.reload.document_capture_submit_count }.from(0).to(1),
)
end
end
end

context 'when doc_auth_document_capture_controller_enabled is false' do
Expand Down
Loading