Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
0672b43
Threat metrix initial page
mdiarra3 Sep 17, 2024
ca28316
Merge remote-tracking branch 'origin/main' into LG-14396-threat-metri…
mdiarra3 Sep 18, 2024
4567c2a
threat metrix helper
mdiarra3 Sep 19, 2024
515d5e2
changelog: Upcoming Features, Account creation, Threat metrix addiition
mdiarra3 Sep 23, 2024
f14704d
Merge remote-tracking branch 'origin/main' into LG-14396-threat-metri…
mdiarra3 Sep 23, 2024
74ad2e0
fix up threatmetrix naming
mdiarra3 Sep 24, 2024
fa122ec
fix true
mdiarra3 Sep 24, 2024
3fd3fcc
fix up rubocop
mdiarra3 Sep 24, 2024
5c3e765
add override for csp for threat metrix
mdiarra3 Sep 24, 2024
4298a2d
fix linting
mdiarra3 Sep 24, 2024
56fcfe4
update to move threatmetrix to idv
mdiarra3 Sep 25, 2024
faa2df7
add new line
mdiarra3 Sep 25, 2024
9ba837d
fix indentationg
mdiarra3 Sep 25, 2024
c9ba668
make threat metrix profiling into a separate partial
mdiarra3 Sep 25, 2024
8eb441f
fix styling indentation
mdiarra3 Sep 26, 2024
1a96823
Threat metrix helper adjustment
mdiarra3 Sep 26, 2024
6792efe
add regtistrations threat metrix spec
mdiarra3 Sep 26, 2024
1a79185
fix ssn controller and spec
mdiarra3 Sep 26, 2024
eab8541
Merge remote-tracking branch 'origin/main' into LG-14525-threatmetrix…
mdiarra3 Sep 26, 2024
ef1fab8
threatmetrix api
mdiarra3 Oct 2, 2024
ee351e8
update to use locals
mdiarra3 Oct 2, 2024
e7fd870
return empty hash
mdiarra3 Oct 3, 2024
e009030
Merge remote-tracking branch 'origin/LG-14396-threat-metrix-create-ac…
mdiarra3 Oct 3, 2024
7171cc0
update proofer
mdiarra3 Oct 4, 2024
8694d68
add device profiling work
mdiarra3 Oct 9, 2024
0ddeaf1
Move device profiling over
mdiarra3 Oct 11, 2024
5f27b67
changelog: Upcoming Features, Authentication Threat Metrix, API creation
mdiarra3 Oct 16, 2024
a259be7
add spec for threat_metrix helper
mdiarra3 Oct 16, 2024
68bd93d
Merge remote-tracking branch 'origin/main' into LG-14525-threatmetrix…
mdiarra3 Oct 16, 2024
eca8a7e
refactor to just parametize proofer
mdiarra3 Oct 23, 2024
580ad9e
fix rubocop issues
mdiarra3 Oct 23, 2024
759b82c
fix initial rspec
mdiarra3 Oct 24, 2024
47c2f72
Merge remote-tracking branch 'origin/main' into LG-14525-threatmetrix…
mdiarra3 Oct 24, 2024
8bb0689
add spec for verification request for authentication
mdiarra3 Oct 28, 2024
3548aab
Merge remote-tracking branch 'origin/main' into LG-14525-threatmetrix…
mdiarra3 Oct 28, 2024
1950862
remove threat metrix call in completions controller
mdiarra3 Oct 28, 2024
d76d8a7
remove registration emails ref to tmx in completions page
mdiarra3 Oct 28, 2024
3e9da1e
fix threatmetrix step helper spec
mdiarra3 Oct 29, 2024
edaa3fe
update analytics event
mdiarra3 Oct 29, 2024
535783a
change to proofing applicant
mdiarra3 Oct 29, 2024
9f66152
fix analytics event lint
mdiarra3 Oct 29, 2024
070ab76
update application yml file
mdiarra3 Oct 29, 2024
2c679e1
remove current sp
mdiarra3 Oct 29, 2024
9661889
use job to query threatmetrix
mdiarra3 Oct 31, 2024
fc1853a
update add mfa
mdiarra3 Oct 31, 2024
c1a98f3
add mfa spec to make sure job is properly called
mdiarra3 Oct 31, 2024
e804b81
fix tests around addmfa call
mdiarra3 Oct 31, 2024
20e1048
put back error response
mdiarra3 Oct 31, 2024
e81dee6
Merge remote-tracking branch 'origin/main' into LG-14525-threatmetrix…
mdiarra3 Nov 4, 2024
6fad48f
remove unneded comments
mdiarra3 Nov 4, 2024
a1fa9a8
update completions with empty space
mdiarra3 Nov 4, 2024
a284d11
remove device profiling result
mdiarra3 Nov 4, 2024
0c7fb63
leverage same verification request
mdiarra3 Nov 4, 2024
d9a9cf1
take into account gsub
mdiarra3 Nov 5, 2024
efb17ed
update verificationr spec
mdiarra3 Nov 5, 2024
28f1cd4
fix rspe
mdiarra3 Nov 5, 2024
c3a8e27
address comment, feature flag toggle, and application.yml
mdiarra3 Nov 6, 2024
0a1fa28
update identity config
mdiarra3 Nov 6, 2024
22ee347
make sure spec is when enabled
mdiarra3 Nov 6, 2024
92fc50f
rubocop
mdiarra3 Nov 6, 2024
4b090f0
move yml comment, fix spacing and clean up specs
mdiarra3 Nov 7, 2024
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
9 changes: 9 additions & 0 deletions app/controllers/concerns/mfa_setup_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ def check_if_possible_piv_user
end
end

def threatmetrix_attrs
{
user_id: current_user.id,
request_ip: request&.remote_ip,
threatmetrix_session_id: session[:threatmetrix_session_id],
email: EmailContext.new(current_user).last_sign_in_email_address.email,
}
end

private

def track_user_registration_mfa_setup_complete_event
Expand Down
1 change: 0 additions & 1 deletion app/controllers/sign_up/completions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ def analytics_attributes(page_occurence)
if page_occurence.present? && DisposableEmailDomain.disposable?(email_domain)
attributes[:disposable_email_domain] = email_domain
end

attributes
end

Expand Down
1 change: 0 additions & 1 deletion app/controllers/sign_up/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ def process_successful_creation
session[:email] = @register_user_email_form.email
session[:terms_accepted] = @register_user_email_form.terms_accepted
session[:sign_in_flow] = :create_account

redirect_to sign_up_verify_email_url(resend: resend_confirmation)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def track_mfa_added
reason: RecaptchaAnnotator::AnnotationReasons::PASSED_TWO_FACTOR,
),
)
Funnel::Registration::AddMfa.call(current_user.id, 'phone', analytics)
Funnel::Registration::AddMfa.call(current_user.id, 'phone', analytics, threatmetrix_attrs)
end

def confirm_multiple_factors_enabled
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/users/backup_code_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@ def track_backup_codes_created
enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count,
in_account_creation_flow: in_account_creation_flow?,
)
Funnel::Registration::AddMfa.call(current_user.id, 'backup_codes', analytics)
Funnel::Registration::AddMfa.call(
current_user.id,
'backup_codes',
analytics,
threatmetrix_attrs,
)
end

def mfa_user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def process_valid_submission

def track_mfa_method_added
analytics.multi_factor_auth_added_piv_cac(**analytics_properties)
Funnel::Registration::AddMfa.call(current_user.id, 'piv_cac', analytics)
Funnel::Registration::AddMfa.call(current_user.id, 'piv_cac', analytics, threatmetrix_attrs)
end

def process_invalid_submission
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/users/totp_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def create_events
enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count,
in_account_creation_flow: in_account_creation_flow?,
)
Funnel::Registration::AddMfa.call(current_user.id, 'auth_app', analytics)
Funnel::Registration::AddMfa.call(current_user.id, 'auth_app', analytics, threatmetrix_attrs)
end

def process_invalid_code
Expand Down
14 changes: 12 additions & 2 deletions app/controllers/users/webauthn_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,23 @@ def process_valid_webauthn(form)
handle_valid_verification_for_confirmation_context(
auth_method: TwoFactorAuthenticatable::AuthMethod::WEBAUTHN_PLATFORM,
)
Funnel::Registration::AddMfa.call(current_user.id, 'webauthn_platform', analytics)
Funnel::Registration::AddMfa.call(
current_user.id,
'webauthn_platform',
analytics,
threatmetrix_attrs,
)
flash[:success] = t('notices.webauthn_platform_configured')
else
handle_valid_verification_for_confirmation_context(
auth_method: TwoFactorAuthenticatable::AuthMethod::WEBAUTHN,
)
Funnel::Registration::AddMfa.call(current_user.id, 'webauthn', analytics)
Funnel::Registration::AddMfa.call(
current_user.id,
'webauthn',
analytics,
threatmetrix_attrs,
)
flash[:success] = t('notices.webauthn_configured')
end
redirect_to next_setup_path || after_mfa_setup_path
Expand Down
24 changes: 24 additions & 0 deletions app/jobs/account_creation_threat_metrix_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

class AccountCreationThreatMetrixJob < ApplicationJob
def perform(
user_id: nil,
threatmetrix_session_id: nil,
request_ip: nil,
email: nil
)

device_profiling_result = AccountCreation::DeviceProfiling.new.proof(
request_ip: request_ip,
threatmetrix_session_id: threatmetrix_session_id,
user_email: email,
)
ensure
user = User.find_by(id: user_id)
analytics(user).account_creation_tmx_result(**device_profiling_result.to_h)
end

def analytics(user)
Analytics.new(user: user, request: nil, session: {}, sp: nil)
end
end
63 changes: 63 additions & 0 deletions app/services/account_creation/device_profiling.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

module AccountCreation
class DeviceProfiling
attr_reader :request_ip,
:threatmetrix_session_id,
:user_email,
:device_profile_result
def proof(
request_ip:,
threatmetrix_session_id:,
user_email:
)
@request_ip = request_ip
@threatmetrix_session_id = threatmetrix_session_id
@user_email = user_email

@device_profile_result = device_profile
end

def device_profile
return threatmetrix_disabled_result unless
FeatureManagement.account_creation_device_profiling_collecting_enabled?
return threatmetrix_id_missing_result if threatmetrix_session_id.blank?

proofer.proof(
threatmetrix_session_id: threatmetrix_session_id,
email: user_email,
request_ip: request_ip,
)
end

def threatmetrix_disabled_result
Proofing::DdpResult.new(
success: true,
client: 'tmx_disabled',
review_status: 'pass',
)
end

def threatmetrix_id_missing_result
Proofing::DdpResult.new(
success: false,
client: 'tmx_session_id_missing',
review_status: 'reject',
)
end

def proofer
@proofer ||=
if IdentityConfig.store.lexisnexis_threatmetrix_mock_enabled
Proofing::Mock::DdpMockClient.new
else
Proofing::LexisNexis::Ddp::Proofer.new(
api_key: IdentityConfig.store.lexisnexis_threatmetrix_api_key,
org_id: IdentityConfig.store.lexisnexis_threatmetrix_org_id,
base_url: IdentityConfig.store.lexisnexis_threatmetrix_base_url,
ddp_policy: IdentityConfig.store.lexisnexis_threatmetrix_authentication_policy,
)
end
end
end
end
40 changes: 40 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,46 @@
# || ||

module AnalyticsEvents
# @param [Boolean] success Check whether threatmetrix succeeded properly.
# @param [String] transaction_id Vendor-specific transaction ID for the request.
# @param [String, nil] client Client user was directed from when creating account
# @param [array<String>, nil] errors error response from api call
# @param [String, nil] exception Error exception from api call
# @param [Boolean] timed_out set whether api call timed out
# @param [String] review_status TMX decision on the user
# @param [String] account_lex_id LexID associated with the response.
# @param [String] session_id Session ID associated with response
# @param [Hash] response_body total response body for api call
# Result when threatmetrix is completed for account creation and result
def account_creation_tmx_result(
client:,
success:,
errors:,
exception:,
timed_out:,
transaction_id:,
review_status:,
account_lex_id:,
session_id:,
response_body:,
**extra
)
track_event(
:account_creation_tmx_result,
client:,
success:,
errors:,
exception:,
timed_out:,
transaction_id:,
review_status:,
account_lex_id:,
session_id:,
response_body:,
**extra,
)
end

# @param [Boolean] success
# When a user submits a form to delete their account
def account_delete_submitted(success:, **extra)
Expand Down
10 changes: 9 additions & 1 deletion app/services/funnel/registration/add_mfa.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@
module Funnel
module Registration
class AddMfa
def self.call(user_id, mfa_method, analytics)
def self.call(user_id, mfa_method, analytics, threatmetrix_attrs)
now = Time.zone.now
funnel = RegistrationLog.create_or_find_by(user_id: user_id)
return if funnel.registered_at.present?

analytics.user_registration_user_fully_registered(mfa_method: mfa_method)
process_threatmetrix_for_user(
threatmetrix_attrs,
)
funnel.update!(registered_at: now)
end

def self.process_threatmetrix_for_user(threatmetrix_attrs)
return unless FeatureManagement.account_creation_device_profiling_collecting_enabled?
AccountCreationThreatMetrixJob.perform_later(**threatmetrix_attrs)
end
end
end
end
1 change: 1 addition & 0 deletions app/services/proofing/lexis_nexis/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module LexisNexis
:request_timeout,
:org_id,
:api_key,
:ddp_policy,
keyword_init: true,
allowed_members: [
:instant_verify_workflow,
Expand Down
28 changes: 14 additions & 14 deletions app/services/proofing/lexis_nexis/ddp/verification_request.rb
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 like this revised direction a lot better 👍

Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,29 @@ def build_request_body
{
api_key: config.api_key,
org_id: config.org_id,
account_address_street1: applicant[:address1],
account_address_street1: applicant[:address1] || '',
account_address_street2: applicant[:address2] || '',
account_address_city: applicant[:city],
account_address_state: applicant[:state],
account_address_country: 'US',
account_address_zip: applicant[:zipcode],
account_address_city: applicant[:city] || '',
account_address_state: applicant[:state] || '',
account_address_country: applicant[:state] ? 'US' : '',
account_address_zip: applicant[:zipcode] || '',
account_date_of_birth: applicant[:dob] ?
Date.parse(applicant[:dob]).strftime('%Y%m%d') : '',
account_email: applicant[:email],
account_first_name: applicant[:first_name],
account_last_name: applicant[:last_name],
account_first_name: applicant[:first_name] || '',
account_last_name: applicant[:last_name] || '',
account_telephone: '', # applicant[:phone], decision was made not to send phone
account_drivers_license_number: applicant[:state_id_number]&.gsub(/\W/, ''),
account_drivers_license_type: 'us_dl',
account_drivers_license_issuer: applicant[:state_id_jurisdiction].to_s.strip,
account_drivers_license_number: applicant[:state_id_number]&.gsub(/\W/, '') || '',
account_drivers_license_type: applicant[:state_id_number] ? 'us_dl' : '',
account_drivers_license_issuer: applicant[:state_id_jurisdiction].to_s.strip || '',
event_type: 'ACCOUNT_CREATION',
policy: IdentityConfig.store.lexisnexis_threatmetrix_policy,
policy: config.ddp_policy,
service_type: 'all',
session_id: applicant[:threatmetrix_session_id],
national_id_number: applicant[:ssn].gsub(/\D/, ''),
national_id_type: 'US_SSN',
national_id_number: applicant[:ssn]&.gsub(/\D/, '') || '',
national_id_type: applicant[:ssn] ? 'US_SSN' : '',
input_ip_address: applicant[:request_ip],
local_attrib_1: applicant[:uuid_prefix],
local_attrib_1: applicant[:uuid_prefix] || '',
}.to_json
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def proofer
api_key: IdentityConfig.store.lexisnexis_threatmetrix_api_key,
org_id: IdentityConfig.store.lexisnexis_threatmetrix_org_id,
base_url: IdentityConfig.store.lexisnexis_threatmetrix_base_url,
ddp_policy: IdentityConfig.store.lexisnexis_threatmetrix_policy,
)
end
end
Expand Down
1 change: 1 addition & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ lexisnexis_request_mode: testing
###################################################################
# LexisNexis DDP/ThreatMetrix #####################################
lexisnexis_threatmetrix_api_key:
lexisnexis_threatmetrix_authentication_policy: '1234'
lexisnexis_threatmetrix_base_url:
lexisnexis_threatmetrix_js_signing_cert: ''
lexisnexis_threatmetrix_mock_enabled: true
Expand Down
2 changes: 1 addition & 1 deletion lib/feature_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def self.recaptcha_enterprise?
IdentityConfig.store.recaptcha_enterprise_project_id.present?
end

# Whether we collect device profiling information as part of the account creation process
# Whether we collect device profiling as part of the account creation process
def self.account_creation_device_profiling_collecting_enabled?
case IdentityConfig.store.account_creation_device_profiling
when :enabled, :collect_only then true
Expand Down
1 change: 1 addition & 0 deletions lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def self.store
config.add(:in_person_stop_expiring_enrollments, type: :boolean)
config.add(:invalid_gpo_confirmation_zipcode, type: :string)
config.add(:lexisnexis_account_id, type: :string)
config.add(:lexisnexis_threatmetrix_authentication_policy, type: :string)
config.add(:lexisnexis_base_url, type: :string)
config.add(:lexisnexis_hmac_auth_enabled, type: :boolean)
config.add(:lexisnexis_hmac_key_id, type: :string)
Expand Down
11 changes: 10 additions & 1 deletion spec/controllers/users/backup_code_setup_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@
end

shared_examples 'valid backup codes creation' do
let(:threatmetrix_attrs) do
{
user_id: user.id,
request_ip: Faker::Internet.ip_v4_address,
threatmetrix_session_id: 'test-session',
email: user.email,
}
end

it 'creates backup codes and logs expected events' do
stub_analytics
allow(controller).to receive(:in_multi_mfa_selection_flow?).and_return(true)

Funnel::Registration::AddMfa.call(user.id, 'phone', @analytics)
Funnel::Registration::AddMfa.call(user.id, 'phone', @analytics, threatmetrix_attrs)
expect(PushNotification::HttpPush).to receive(:deliver).
with(PushNotification::RecoveryInformationChangedEvent.new(user: user))

Expand Down
Loading