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
2 changes: 2 additions & 0 deletions app/controllers/concerns/idv/verify_info_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ def create_fraud_review_request_if_needed(result)
success = (threatmetrix_result[:review_status] == 'pass')

attempts_api_tracker.idv_device_risk_assessment(
device_fingerprint: threatmetrix_result.dig(:device_fingerprint),
success:,
failure_reason: device_risk_failure_reason(success, threatmetrix_result),
)
Expand Down Expand Up @@ -372,6 +373,7 @@ def delete_threatmetrix_response_body(form_response)
)
return if threatmetrix_result.blank?

threatmetrix_result.delete(:device_fingerprint)
threatmetrix_result.delete(:response_body)
end

Expand Down
4 changes: 3 additions & 1 deletion app/services/attempts_api/tracker_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -527,12 +527,14 @@ def mfa_submission_code_rate_limited(mfa_device_type:)
end

# @param [Boolean] success True means TMX's device risk check has a 'pass' review status
# @param [String] device_fingerprint 32-character string based on device attributes
# @param [Hash<Symbol,Array<Symbol>>] failure_reason
# Tracks the result of the Device fraud check during Identity Verification
def idv_device_risk_assessment(success:, failure_reason: nil)
def idv_device_risk_assessment(success:, device_fingerprint: nil, failure_reason: nil)
track_event(
'idv-device-risk-assessment',
success:,
device_fingerprint:,
failure_reason:,
)
end
Expand Down
4 changes: 4 additions & 0 deletions app/services/proofing/ddp_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ def to_h
}
end

def device_fingerprint
response_body&.dig(:fuzzy_device_id)
end

private

def redacted_response_body
Expand Down
8 changes: 7 additions & 1 deletion app/services/proofing/resolution/result_adjudicator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def adjudicated_result
resolution: resolution_result.to_h,
residential_address: residential_resolution_result.to_h,
state_id: state_id_result.to_h,
threatmetrix: device_profiling_result.to_h,
threatmetrix:,
phone_precheck: phone_finder_result.to_h,
},
},
Expand Down Expand Up @@ -83,6 +83,12 @@ def exception
device_profiling_result.exception
end

def threatmetrix
device_profiling_result.to_h.merge(
device_fingerprint: device_profiling_result.device_fingerprint,
)
end

def timed_out?
resolution_result.timed_out? ||
residential_resolution_result.timed_out? ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ allOf:
- $ref: "../shared/EventProperties.yml"
- type: object
properties:
device_fingerprint:
type: string
description: |
A 32-character string based exclusively on device attributes to improve detection of returning visitors, especially those trying to elude identification.
failure_reason:
type: object
description: |
Expand Down Expand Up @@ -38,4 +42,4 @@ allOf:
success:
type: boolean
description: |
Indicates whether the TMX response status pass.
Indicates whether the user has passed the device risk assessment.
34 changes: 34 additions & 0 deletions spec/controllers/idv/verify_info_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
let(:review_status) { 'pass' }
let(:applicant_pii) { Idp::Constants::MOCK_IDV_APPLICANT_WITH_SSN }
let(:success) { true }
let(:device_fingerprint) { SecureRandom.hex(32) }

let(:idv_result) do
{
Expand All @@ -196,6 +197,7 @@
stages: {
threatmetrix: {
success:,
device_fingerprint:,
client: threatmetrix_client_id,
transaction_id: 1,
review_status: review_status,
Expand Down Expand Up @@ -283,6 +285,19 @@
),
),
)
expect(@analytics).to have_logged_event(
'IdV: doc auth verify proofing results',
hash_including(
proofing_results: hash_including(
context: hash_including(
stages: hash_including(
threatmetrix: hash_excluding(device_fingerprint:),
),
),
),
),
)

expect(@analytics).to have_logged_event(
:idv_threatmetrix_response_body,
response_body: hash_including(
Expand All @@ -294,6 +309,7 @@
it 'tracks the attempts events' do
expect(@attempts_api_tracker).to receive(:idv_device_risk_assessment).with(
success: true,
device_fingerprint:,
failure_reason: nil,
)
expect(@attempts_api_tracker).to receive(:idv_verification_submitted).with(
Expand Down Expand Up @@ -330,6 +346,7 @@
it 'tracks a failed tmx fraud check' do
expect(@attempts_api_tracker).to receive(:idv_device_risk_assessment).with(
success: false,
device_fingerprint:,
failure_reason: { fraud_risk_summary_reason_code: ['Identity_Negative_History'] },
)

Expand Down Expand Up @@ -371,6 +388,7 @@
stages: {
threatmetrix: {
client: nil,
device_fingerprint:,
errors: {},
exception: "Unexpected ThreatMetrix review_status value: #{review_status}",
response_body: nil,
Expand Down Expand Up @@ -420,6 +438,19 @@
),
),
)

expect(@analytics).to have_logged_event(
'IdV: doc auth verify proofing results',
hash_including(
proofing_results: hash_including(
context: hash_including(
stages: hash_including(
threatmetrix: hash_excluding(device_fingerprint:),
),
),
),
),
)
end

it 'tracks the event for the attempts api' do
Expand Down Expand Up @@ -451,6 +482,7 @@
stub_attempts_tracker
expect(@attempts_api_tracker).to receive(:idv_device_risk_assessment).with(
success: false,
device_fingerprint:,
failure_reason: {
fraud_risk_summary_reason_code:
['Fraud risk assessment has failed for unknown reasons'],
Expand All @@ -473,6 +505,7 @@
it 'tracks a failed tmx fraud check' do
expect(@attempts_api_tracker).to receive(:idv_device_risk_assessment).with(
success:,
device_fingerprint:,
failure_reason: {
fraud_risk_summary_reason_code: ['Identity_Negative_History'],
},
Expand Down Expand Up @@ -516,6 +549,7 @@
stub_attempts_tracker
expect(@attempts_api_tracker).to receive(:idv_device_risk_assessment).with(
success: false,
device_fingerprint:,
failure_reason: {
fraud_risk_summary_reason_code: ['Identity_Negative_History'],
},
Expand Down
28 changes: 26 additions & 2 deletions spec/services/proofing/ddp_result_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@
context 'when provided' do
it 'is present' do
transaction_id = 'foo'
result = Proofing::DdpResult.new
result.transaction_id = transaction_id
result = Proofing::DdpResult.new(transaction_id:)
expect(result.transaction_id).to eq(transaction_id)
end
end
Expand Down Expand Up @@ -144,4 +143,29 @@
end
end
end

describe '#device_fingerprint' do
let(:response_body) { { fuzzy_device_id: '12345' } }
subject { described_class.new(response_body:) }

context 'when response_body is present' do
it 'returns the device fingerprint' do
expect(subject.device_fingerprint).to eq('12345')
end
end

context 'when response_body is nil' do
let(:response_body) { nil }
it 'returns nil' do
expect(subject.device_fingerprint).to be_nil
end
end

context 'when response_body does not contain fuzzy_device_id' do
let(:response_body) { { some_other_key: 'value' } }
it 'returns nil' do
expect(subject.device_fingerprint).to be_nil
end
end
end
end