app/controllers/concerns/idv/verify_info_concern.rb
# frozen_string_literal: true
module Idv
module VerifyInfoConcern
extend ActiveSupport::Concern
STEP_NAME = 'verify_info'
def shared_update
return if idv_session.verify_info_step_document_capture_session_uuid
analytics.idv_doc_auth_verify_submitted(**analytics_arguments)
Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).
call('verify', :update, true)
set_state_id_type
ssn_rate_limiter.increment!
document_capture_session = DocumentCaptureSession.create(
user_id: current_user.id,
issuer: sp_session[:issuer],
)
document_capture_session.requested_at = Time.zone.now
idv_session.verify_info_step_document_capture_session_uuid = document_capture_session.uuid
Idv::Agent.new(pii).proof_resolution(
document_capture_session,
trace_id: amzn_trace_id,
user_id: current_user.id,
threatmetrix_session_id: idv_session.threatmetrix_session_id,
request_ip: request.remote_ip,
ipp_enrollment_in_progress: ipp_enrollment_in_progress?,
)
return true
end
private
def ipp_enrollment_in_progress?
current_user.has_in_person_enrollment?
end
def resolution_rate_limiter
@resolution_rate_limiter ||= RateLimiter.new(
user: current_user,
rate_limit_type: :idv_resolution,
)
end
def ssn_rate_limiter
@ssn_rate_limiter ||= RateLimiter.new(
target: Pii::Fingerprinter.fingerprint(idv_session.ssn),
rate_limit_type: :proof_ssn,
)
end
def idv_failure(result)
proofing_results_exception = result.extra.dig(:proofing_results, :exception)
is_mva_exception = result.extra.dig(
:proofing_results,
:context,
:stages,
:state_id,
:mva_exception,
)
if ssn_rate_limiter.limited?
idv_failure_log_rate_limited(:proof_ssn)
redirect_to idv_session_errors_ssn_failure_url
elsif resolution_rate_limiter.limited?
idv_failure_log_rate_limited(:idv_resolution)
redirect_to rate_limited_url
elsif proofing_results_exception.present? && is_mva_exception
idv_failure_log_warning
redirect_to state_id_warning_url
elsif proofing_results_exception.present?
idv_failure_log_error
redirect_to exception_url
else
idv_failure_log_warning
redirect_to warning_url
end
end
def idv_failure_log_rate_limited(rate_limit_type)
if rate_limit_type == :proof_ssn
analytics.rate_limit_reached(
limiter_type: :proof_ssn,
step_name: STEP_NAME,
)
elsif rate_limit_type == :idv_resolution
analytics.rate_limit_reached(
limiter_type: :idv_resolution,
step_name: STEP_NAME,
)
end
end
def idv_failure_log_error
analytics.idv_doc_auth_exception_visited(
step_name: STEP_NAME,
remaining_submit_attempts: resolution_rate_limiter.remaining_count,
)
end
def idv_failure_log_warning
analytics.idv_doc_auth_warning_visited(
step_name: STEP_NAME,
remaining_submit_attempts: resolution_rate_limiter.remaining_count,
)
end
def rate_limited_url
idv_session_errors_failure_url
end
def exception_url
idv_session_errors_exception_url(flow: flow_param)
end
def state_id_warning_url
idv_session_errors_state_id_warning_url(flow: flow_param)
end
def warning_url
idv_session_errors_warning_url(flow: flow_param)
end
def process_async_state(current_async_state)
if current_async_state.done?
async_state_done(current_async_state)
return
end
if current_async_state.in_progress?
render 'shared/wait'
return
end
return if confirm_not_rate_limited_after_doc_auth
if current_async_state.none?
render :show
elsif current_async_state.missing?
analytics.idv_proofing_resolution_result_missing
flash.now[:error] = I18n.t('idv.failure.timeout')
render :show
delete_async
end
end
def async_state_done(current_async_state)
create_fraud_review_request_if_needed(current_async_state.result)
form_response = idv_result_to_form_response(
result: current_async_state.result,
state: pii[:state],
state_id_jurisdiction: pii[:state_id_jurisdiction],
state_id_number: pii[:state_id_number],
# todo: add other edited fields?
extra: {
address_edited: !!idv_session.address_edited,
address_line2_present: !pii[:address2].blank?,
pii_like_keypaths: [
[:errors, :ssn],
[:proofing_results, :context, :stages, :resolution, :errors, :ssn],
[:proofing_results, :context, :stages, :residential_address, :errors, :ssn],
[:proofing_results, :context, :stages, :threatmetrix, :response_body, :first_name],
[:same_address_as_id],
[:proofing_results, :context, :stages, :state_id, :state_id_jurisdiction],
],
},
)
summarize_result_and_rate_limit(form_response)
delete_async
if form_response.success?
save_threatmetrix_status(form_response)
move_applicant_to_idv_session
idv_session.mark_verify_info_step_complete!
flash[:success] = t('doc_auth.forms.doc_success')
redirect_to next_step_url
end
analytics.idv_doc_auth_verify_proofing_results(**analytics_arguments, **form_response.to_h)
end
def next_step_url
return idv_request_letter_url if FeatureManagement.idv_by_mail_only?
idv_phone_url
end
def save_threatmetrix_status(form_response)
review_status = form_response.extra.dig(:proofing_results, :threatmetrix_review_status)
idv_session.threatmetrix_review_status = review_status
end
def summarize_result_and_rate_limit(summary_result)
proofing_results_exception = summary_result.extra.dig(:proofing_results, :exception)
resolution_rate_limiter.increment! if proofing_results_exception.blank?
if summary_result.success?
add_proofing_components
else
idv_failure(summary_result)
end
end
def add_proofing_components
ProofingComponent.create_or_find_by(user: current_user).update(
resolution_check: Idp::Constants::Vendors::LEXIS_NEXIS,
source_check: Idp::Constants::Vendors::AAMVA,
)
end
def load_async_state
dcs_uuid = idv_session.verify_info_step_document_capture_session_uuid
dcs = DocumentCaptureSession.find_by(uuid: dcs_uuid)
return ProofingSessionAsyncResult.none if dcs_uuid.nil?
return ProofingSessionAsyncResult.missing if dcs.nil?
proofing_job_result = dcs.load_proofing_result
return ProofingSessionAsyncResult.missing if proofing_job_result.nil?
proofing_job_result
end
def delete_async
idv_session.verify_info_step_document_capture_session_uuid = nil
end
def idv_result_to_form_response(
result:,
state: nil,
state_id_jurisdiction: nil,
state_id_number: nil,
extra: {}
)
state_id = result.dig(:context, :stages, :state_id)
if state_id
state_id[:state] = state if state
state_id[:state_id_jurisdiction] = state_id_jurisdiction if state_id_jurisdiction
if state_id_number
state_id[:state_id_number] =
StringRedacter.redact_alphanumeric(state_id_number)
end
end
FormResponse.new(
success: result[:success],
errors: result[:errors],
extra: extra.merge(proofing_results: result.except(:errors, :success)),
)
end
def create_fraud_review_request_if_needed(result)
return unless FeatureManagement.proofing_device_profiling_collecting_enabled?
threatmetrix_result = result.dig(:context, :stages, :threatmetrix)
return unless threatmetrix_result
return if threatmetrix_result[:review_status] == 'pass'
FraudReviewRequest.create(
user: current_user,
login_session_id: Digest::SHA1.hexdigest(current_user.unique_session_id.to_s),
)
end
def move_applicant_to_idv_session
idv_session.applicant = pii
idv_session.applicant[:ssn] = idv_session.ssn
idv_session.applicant['uuid'] = current_user.uuid
end
def add_cost(token, transaction_id: nil)
Db::SpCost::AddSpCost.call(current_sp, token, transaction_id: transaction_id)
end
end
end