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
5 changes: 4 additions & 1 deletion app/controllers/idv/hybrid_handoff_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ def show
)
@post_office_enabled = IdentityConfig.store.in_person_proofing_enabled &&
IdentityConfig.store.in_person_proofing_opt_in_enabled &&
IdentityConfig.store.in_person_doc_auth_button_enabled
IdentityConfig.store.in_person_doc_auth_button_enabled &&
Idv::InPersonConfig.enabled_for_issuer?(
decorated_sp_session.sp_issuer,
)
@selfie_required = idv_session.selfie_check_required
@idv_how_to_verify_form = Idv::HowToVerifyForm.new
set_how_to_verify_presenter
Expand Down
17 changes: 12 additions & 5 deletions app/forms/idv/api_image_upload_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ def submit

if client_response.success?
doc_pii_response = validate_pii_from_doc(client_response)
if doc_pii_response.success? && passport_submittal

if doc_pii_response.success? && passport_requested? && passport_submittal
mrz_response = validate_mrz(client_response)
end
end
Expand Down Expand Up @@ -185,7 +186,7 @@ def post_images_to_client
def document_type
return nil if document_capture_session.nil?

@document_type ||= document_capture_session.passport_requested? \
@document_type ||= passport_requested? \
? 'Passport' : 'DriversLicense'
end

Expand Down Expand Up @@ -289,6 +290,7 @@ def determine_response(form_response:, client_response:, doc_pii_response:, mrz_
# doc_pii validation failed
return doc_pii_response if doc_pii_response.present? && !doc_pii_response.success?

# mrz validation failed
return mrz_response if mrz_response.present? && !mrz_response.success?

client_response
Expand Down Expand Up @@ -507,6 +509,10 @@ def user_uuid
document_capture_session&.user&.uuid
end

def passport_requested?
!!document_capture_session&.passport_requested?
end

def rate_limiter
@rate_limiter ||= RateLimiter.new(
user: document_capture_session.user,
Expand Down Expand Up @@ -535,11 +541,12 @@ def store_failed_images(client_response, doc_pii_response)
}
end
# doc auth failed due to non network error or doc_pii is not valid
failed_front_fingerprint = nil
failed_back_fingerprint = nil
failed_passport_fingerprint = nil

if client_response && !client_response.success? && !client_response.network_error?
errors_hash = client_response.errors&.to_h || {}
failed_front_fingerprint = nil
failed_back_fingerprint = nil
failed_passport_fingerprint = nil

if errors_hash[:front] || errors_hash[:back] || errors_hash[:passport]
if errors_hash[:front]
Expand Down
4 changes: 0 additions & 4 deletions app/presenters/duplicate_profiles_detected_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ def heading
I18n.t('duplicate_profiles_detected.heading')
end

def intro
I18n.t('duplicate_profiles_detected.intro', app_name: APP_NAME)
end

def associated_profiles
profile_ids = [user.active_profile] + user_session[:duplicate_profile_ids]
profiles = Profile.where(id: profile_ids)
Expand Down
2 changes: 1 addition & 1 deletion app/services/idv/proofing_components.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def document_check
end

def document_type
return 'state_id' if idv_session.remote_document_capture_complete?
idv_session.pii_from_doc&.id_doc_type
end

def source_check
Expand Down
14 changes: 13 additions & 1 deletion app/views/duplicate_profiles_detected/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@
</ul>
</div>
<div class="margin-top-2">
<%= link_to(t('duplicate_profiles_detected.dont_recognize_account'), '/') %>
<%= render ButtonComponent.new(
url: root_url,
method: :get,
unstyled: true,
).with_content(t('duplicate_profiles_detected.dont_recognize_account')) %>
</div>

<div class="margin-top-4 grid-row">
Expand All @@ -58,6 +62,14 @@
class: 'usa-button usa-button usa-button--outline usa-button--wide usa-button--big',
).with_content(t('duplicate_profiles_detected.sign_out')) %>
</div>

<div class="margin-top-2">
<%= render ButtonComponent.new(
url: root_url,
method: :get,
unstyled: true,
).with_content(t('duplicate_profiles_detected.cant_access')) %>
</div>
<% end %>


9 changes: 5 additions & 4 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,8 @@ doc_auth.tips.document_capture_selfie_text1: Remove any items covering your face
doc_auth.tips.document_capture_selfie_text2: Take photo in a well-lit place
doc_auth.tips.document_capture_selfie_text3: Keep your expression neutral
doc_auth.tips.document_capture_selfie_text4: Make sure your whole face is visible within the green circle
duplicate_profiles_detected.accounts_list.heading: Accounts with the same SSN
duplicate_profiles_detected.accounts_list.heading: Accounts with the same verified information
duplicate_profiles_detected.cant_access: 'I can’t access an account'
duplicate_profiles_detected.connected_acct_html: '<strong> Connected agencies: </strong> %{count}'
duplicate_profiles_detected.created_at_html: '<strong> Created: </strong> %{timestamp_html}'
duplicate_profiles_detected.delete_duplicates.details_html: Sign in, authenticate, and delete the account from the ‘Your account’ page. %{link_html}
Expand All @@ -743,14 +744,14 @@ duplicate_profiles_detected.delete_duplicates.link: How to delete your account.
duplicate_profiles_detected.dont_recognize_account: I don’t recognize an account above
duplicate_profiles_detected.duplicate: Duplicate
duplicate_profiles_detected.get_help: Get Help
duplicate_profiles_detected.heading: You have multiple accounts with the same SSN
duplicate_profiles_detected.intro: For security purposes, you can only verify your identity on one %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.heading: We found other accounts that may be yours
duplicate_profiles_detected.intro: The %{app_name} requires that you only have one identity verified %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.intro2: 'You need to delete the duplicate accounts before signing into %{app_name}. Here’s what to do:'
duplicate_profiles_detected.last_sign_in_at_html: '<strong> Last login: </strong> %{timestamp_html}'
duplicate_profiles_detected.never_logged_in: Never logged in
duplicate_profiles_detected.select_an_account.details: Keep the account that you’ve connected to the most agencies. That way you don’t have to reconnect to all the agencies you use.
duplicate_profiles_detected.select_an_account.heading: Choose an account to keep
duplicate_profiles_detected.sign_back_in.details: Once you’ve deleted the duplicate accounts return to %{app_name} and sign in.
duplicate_profiles_detected.sign_back_in.details: Go back to the %{app_name} website and sign in using the one %{app_name} account you kept.
duplicate_profiles_detected.sign_back_in.heading: Sign back into %{app_name} with one account
duplicate_profiles_detected.sign_out: Sign out
duplicate_profiles_detected.signed_in: Signed In
Expand Down
9 changes: 5 additions & 4 deletions config/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,8 @@ doc_auth.tips.document_capture_selfie_text1: Quite cualquier prenda o accesorio
doc_auth.tips.document_capture_selfie_text2: Tómese la foto en un lugar bien iluminado
doc_auth.tips.document_capture_selfie_text3: Mantenga una expresión neutral
doc_auth.tips.document_capture_selfie_text4: Revise que se vea su rostro completo dentro del círculo verde.
duplicate_profiles_detected.accounts_list.heading: Accounts with the same SSN
duplicate_profiles_detected.accounts_list.heading: Accounts with the same verified information
duplicate_profiles_detected.cant_access: 'I can’t access an account'
duplicate_profiles_detected.connected_acct_html: '<strong> Connected agencies: </strong> %{count}'
duplicate_profiles_detected.created_at_html: '<strong> Created: </strong> %{timestamp_html}'
duplicate_profiles_detected.delete_duplicates.details_html: Sign in, authenticate, and delete the account from the ‘Your account’ page. %{link_html}
Expand All @@ -754,14 +755,14 @@ duplicate_profiles_detected.delete_duplicates.link: How to delete your account.
duplicate_profiles_detected.dont_recognize_account: I don’t recognize an account above
duplicate_profiles_detected.duplicate: Duplicate
duplicate_profiles_detected.get_help: Get Help
duplicate_profiles_detected.heading: You have multiple accounts with the same SSN
duplicate_profiles_detected.intro: For security purposes, you can only verify your identity on one %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.heading: We found other accounts that may be yours
duplicate_profiles_detected.intro: The %{app_name} requires that you only have one identity verified %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.intro2: 'You need to delete the duplicate accounts before signing into %{app_name}. Here’s what to do:'
duplicate_profiles_detected.last_sign_in_at_html: '<strong> Last login: </strong> %{timestamp_html}'
duplicate_profiles_detected.never_logged_in: Never logged in
duplicate_profiles_detected.select_an_account.details: Keep the account that you’ve connected to the most agencies. That way you don’t have to reconnect to all the agencies you use.
duplicate_profiles_detected.select_an_account.heading: Choose an account to keep
duplicate_profiles_detected.sign_back_in.details: Once you’ve deleted the duplicate accounts return to %{app_name} and sign in.
duplicate_profiles_detected.sign_back_in.details: Go back to the %{app_name} website and sign in using the one %{app_name} account you kept.
duplicate_profiles_detected.sign_back_in.heading: Sign back into %{app_name} with one account
duplicate_profiles_detected.sign_out: Sign out
duplicate_profiles_detected.signed_in: Signed In
Expand Down
9 changes: 5 additions & 4 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,8 @@ doc_auth.tips.document_capture_selfie_text1: Enlevez tout ce qui cache votre vis
doc_auth.tips.document_capture_selfie_text2: Prenez votre photo dans un endroit bien éclairé
doc_auth.tips.document_capture_selfie_text3: Gardez une expression neutre
doc_auth.tips.document_capture_selfie_text4: Assurez-vous que l’ensemble de votre visage est visible dans le cercle vert
duplicate_profiles_detected.accounts_list.heading: Accounts with the same SSN
duplicate_profiles_detected.accounts_list.heading: Accounts with the same verified information
duplicate_profiles_detected.cant_access: 'I can’t access an account'
duplicate_profiles_detected.connected_acct_html: '<strong> Connected agencies: </strong> %{count}'
duplicate_profiles_detected.created_at_html: '<strong> Created: </strong> %{timestamp_html}'
duplicate_profiles_detected.delete_duplicates.details_html: Sign in, authenticate, and delete the account from the ‘Your account’ page. %{link_html}
Expand All @@ -743,14 +744,14 @@ duplicate_profiles_detected.delete_duplicates.link: How to delete your account.
duplicate_profiles_detected.dont_recognize_account: I don’t recognize an account above
duplicate_profiles_detected.duplicate: Duplicate
duplicate_profiles_detected.get_help: Get Help
duplicate_profiles_detected.heading: You have multiple accounts with the same SSN
duplicate_profiles_detected.intro: For security purposes, you can only verify your identity on one %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.heading: We found other accounts that may be yours
duplicate_profiles_detected.intro: The %{app_name} requires that you only have one identity verified %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.intro2: 'You need to delete the duplicate accounts before signing into %{app_name}. Here’s what to do:'
duplicate_profiles_detected.last_sign_in_at_html: '<strong> Last login: </strong> %{timestamp_html}'
duplicate_profiles_detected.never_logged_in: Never logged in
duplicate_profiles_detected.select_an_account.details: Keep the account that you’ve connected to the most agencies. That way you don’t have to reconnect to all the agencies you use.
duplicate_profiles_detected.select_an_account.heading: Choose an account to keep
duplicate_profiles_detected.sign_back_in.details: Once you’ve deleted the duplicate accounts return to %{app_name} and sign in.
duplicate_profiles_detected.sign_back_in.details: Go back to the %{app_name} website and sign in using the one %{app_name} account you kept.
duplicate_profiles_detected.sign_back_in.heading: Sign back into %{app_name} with one account
duplicate_profiles_detected.sign_out: Sign out
duplicate_profiles_detected.signed_in: Signed In
Expand Down
9 changes: 5 additions & 4 deletions config/locales/zh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,8 @@ doc_auth.tips.document_capture_selfie_text1: 摘掉任何遮盖您面孔的东
doc_auth.tips.document_capture_selfie_text2: 在光线明亮的地方拍照
doc_auth.tips.document_capture_selfie_text3: 保持中性表情
doc_auth.tips.document_capture_selfie_text4: 确保您整个面孔都可以在绿色圆圈里看到
duplicate_profiles_detected.accounts_list.heading: Accounts with the same SSN
duplicate_profiles_detected.accounts_list.heading: Accounts with the same verified information
duplicate_profiles_detected.cant_access: 'I can’t access an account'
duplicate_profiles_detected.connected_acct_html: '<strong> Connected agencies: </strong> %{count}'
duplicate_profiles_detected.created_at_html: '<strong> Created: </strong> %{timestamp_html}'
duplicate_profiles_detected.delete_duplicates.details_html: Sign in, authenticate, and delete the account from the ‘Your account’ page. %{link_html}
Expand All @@ -754,14 +755,14 @@ duplicate_profiles_detected.delete_duplicates.link: How to delete your account.
duplicate_profiles_detected.dont_recognize_account: I don’t recognize an account above
duplicate_profiles_detected.duplicate: Duplicate
duplicate_profiles_detected.get_help: Get Help
duplicate_profiles_detected.heading: You have multiple accounts with the same SSN
duplicate_profiles_detected.intro: For security purposes, you can only verify your identity on one %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.heading: We found other accounts that may be yours
duplicate_profiles_detected.intro: The %{app_name} requires that you only have one identity verified %{app_name} account. Learn more about duplicate accounts
duplicate_profiles_detected.intro2: 'You need to delete the duplicate accounts before signing into %{app_name}. Here’s what to do:'
duplicate_profiles_detected.last_sign_in_at_html: '<strong> Last login: </strong> %{timestamp_html}'
duplicate_profiles_detected.never_logged_in: Never logged in
duplicate_profiles_detected.select_an_account.details: Keep the account that you’ve connected to the most agencies. That way you don’t have to reconnect to all the agencies you use.
duplicate_profiles_detected.select_an_account.heading: Choose an account to keep
duplicate_profiles_detected.sign_back_in.details: Once you’ve deleted the duplicate accounts return to %{app_name} and sign in.
duplicate_profiles_detected.sign_back_in.details: Go back to the %{app_name} website and sign in using the one %{app_name} account you kept.
duplicate_profiles_detected.sign_back_in.heading: Sign back into %{app_name} with one account
duplicate_profiles_detected.sign_out: Sign out
duplicate_profiles_detected.signed_in: Signed In
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AddNotesToDeviceProfilingResult < ActiveRecord::Migration[8.0]
def up
add_column :device_profiling_results, :notes, :string, comment: 'sensitive=false'
end

def down
remove_column :device_profiling_results, :notes
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2025_06_11_195441) do
ActiveRecord::Schema[8.0].define(version: 2025_07_14_184424) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "pg_catalog.plpgsql"
Expand Down Expand Up @@ -96,6 +96,7 @@
t.string "profiling_type", comment: "sensitive=false"
t.datetime "created_at", null: false, comment: "sensitive=false"
t.datetime "updated_at", null: false, comment: "sensitive=false"
t.string "notes", comment: "sensitive=false"
t.index ["user_id"], name: "index_device_profiling_results_on_user_id"
end

Expand Down
3 changes: 2 additions & 1 deletion docs/attempts-api/schemas/events/shared/EventProperties.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ properties:
description: Known more commonly as ephemeral port numbers associated with the Client IP – This is NOT the CSP server port 443 or 80.
device_id:
type: string
description: Cookie device unique identifier. This value is securely randomly generated server-side as 64 bytes and displayed as a string of hexadecimal characters.
description: |
Cookie device unique identifier. This value is securely randomly generated server-side as a string of 128 hexadecimal characters using the SecureRandom Ruby library.
google_analytics_cookies:
type: object
description: |
Expand Down
23 changes: 23 additions & 0 deletions lib/idp/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,29 @@ module Vendors
zipcode: '59010-1234',
}.freeze

MOCK_IDV_APPLICANT_STATE_ID = {
address1: '1 FAKE RD',
address2: '',
city: 'GREAT FALLS',
dob: '1938-10-06',
eye_color: nil,
first_name: 'FAKEY',
height: 72,
issuing_country_code: 'US',
last_name: 'MCFAKERSON',
middle_name: nil,
name_suffix: 'JR',
state: MOCK_IDV_APPLICANT_STATE,
state_id_expiration: '2099-12-31',
state_id_issued: '2019-12-31',
state_id_jurisdiction: MOCK_IDV_APPLICANT_STATE_ID_JURISDICTION,
state_id_number: '1111111111111',
id_doc_type: 'state_id',
sex: 'male',
weight: nil,
zipcode: '59010-1234',
}.freeze

MOCK_IPP_APPLICANT = {
first_name: 'FAKEY',
last_name: 'MCFAKERSON',
Expand Down
79 changes: 79 additions & 0 deletions lib/tasks/device_profiling.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

namespace :device_profiling do
desc 'Approve rejected device profiling results to pass for list of UUIDs'
task :approve_rejected_users, [:user_uuids] => :environment do |_task, _args|
user_uuids = ARGV[1]

if user_uuids.blank?
puts 'Error: user_uuids is required'
exit 1
end

# Parse UUIDs
uuid_list = user_uuids.split(',').map(&:strip).reject(&:blank?)

puts "Processing #{uuid_list.count} user UUID(s)"
puts "Action: Change 'reject' to 'pass' (skip if already 'pass')"
puts ''

total_users_processed = 0
total_results_updated = 0
skipped_already_passed = 0
users_with_no_results = 0

uuid_list.each do |user_uuid|
total_users_processed += 1

begin
# Find user by UUID
user = User.find_by(uuid: user_uuid)
if user.blank?
puts "User not found: #{user_uuid}"
next
end

# Find device profiling results for this user (reject or pass)
result = DeviceProfilingResult.where(
user_id: user.id,
profiling_type: DeviceProfilingResult::PROFILING_TYPES[:account_creation],
).first

if result.nil?
users_with_no_results += 1
puts "No device profiling results found for: #{user_uuid} (#{user.email})"
next
end

# Check if already passed

if result.review_status == 'pass'
skipped_already_passed += 1
puts "Already passed: #{user_uuid}"
next
end

# Update rejected results to pass
puts "Updating rejected result for: #{user_uuid}"
result.update!(review_status: 'pass', notes: 'Manually overridden')
total_results_updated += 1

puts "Successfully updated result for: #{user_uuid}"

# Log for audit
rescue => e
puts "Error processing #{user_uuid}: #{e.message}"
end
end

puts ''
puts '=' * 80
puts 'SUMMARY:'
puts "Total users processed: #{total_users_processed}"
puts "Results updated (reject → pass): #{total_results_updated}"
puts "Users already passed: #{skipped_already_passed}"
puts "Users with no results: #{users_with_no_results}"

puts 'Task completed successfully!'
end
end
2 changes: 1 addition & 1 deletion spec/controllers/idv/link_sent_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
idv_session: subject.idv_session,
)
expect(proofing_components.document_check).to eq('mock')
expect(proofing_components.document_type).to eq('state_id')
expect(proofing_components.document_type).to eq('drivers_license')
end

context 'redo document capture' do
Expand Down
Loading