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: 1 addition & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ def after_sign_in_path_for(_user)
return user_please_call_url if current_user.suspended?
return user_password_compromised_url if session[:redirect_to_password_compromised].present?
return authentication_methods_setup_url if user_needs_sp_auth_method_setup?
return login_add_piv_cac_prompt_url if session[:needs_to_setup_piv_cac_after_sign_in].present?
return fix_broken_personal_key_url if current_user.broken_personal_key?
return user_session.delete(:stored_location) if user_session.key?(:stored_location)
return login_add_piv_cac_prompt_url if session[:needs_to_setup_piv_cac_after_sign_in].present?
return reactivate_account_url if user_needs_to_reactivate_account?
return login_piv_cac_recommended_path if user_recommended_for_piv_cac?
return second_mfa_reminder_url if user_needs_second_mfa_reminder?
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/idv/in_person/usps_locations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,34 @@ def update
enrollment.update!(
selected_location_details: update_params.as_json,
issuer: current_sp&.issuer,
doc_auth_result: document_capture_session&.last_doc_auth_result,
)

add_proofing_component

render json: { success: true }, status: :ok
end

private

def idv_session
if user_session && current_user
@idv_session ||= Idv::Session.new(
user_session: user_session,
current_user: current_user,
service_provider: current_sp,
)
end
end

def document_capture_session
if idv_session&.document_capture_session_uuid # standard flow
DocumentCaptureSession.find_by(uuid: idv_session.document_capture_session_uuid)
else # hybrid flow
super
end
end

def proofer
@proofer ||= EnrollmentHelper.usps_proofer
end
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/sign_up/completions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ def analytics_attributes(page_occurence)
needs_completion_screen_reason: needs_completion_screen_reason,
}

if (last_enrollment = current_user.in_person_enrollments.last)
attributes[:in_person_proofing_status] = last_enrollment.status
attributes[:doc_auth_result] = last_enrollment.doc_auth_result
end

if page_occurence.present? && DisposableEmailDomain.disposable?(email_domain)
attributes[:disposable_email_domain] = email_domain
end
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def increment_session_bad_password_count
end

def process_locked_out_session
warden.logout(:user)
sign_out(:user)
warden.lock!

flash[:error] = t(
Expand Down Expand Up @@ -100,7 +100,7 @@ def valid_captcha_result?

def process_failed_captcha
flash[:error] = t('errors.messages.invalid_recaptcha_token')
warden.logout(:user)
sign_out(:user)
warden.lock!
redirect_to root_url
end
Expand Down Expand Up @@ -176,6 +176,7 @@ def track_authentication_attempt(email)
bad_password_count: session[:bad_password_count].to_i,
sp_request_url_present: sp_session[:request_url].present?,
remember_device: remember_device_cookie.present?,
new_device: success ? new_device? : nil,
)
end

Expand Down
4 changes: 4 additions & 0 deletions app/forms/idv/api_image_upload_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def submit
if form_response.success?
client_response = post_images_to_client

document_capture_session.update!(
last_doc_auth_result: client_response.extra[:doc_auth_result],
)

if client_response.success?
doc_pii_response = validate_pii_from_doc(client_response)
end
Expand Down
2 changes: 1 addition & 1 deletion app/forms/openid_connect_token_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class OpenidConnectTokenForm
include ActionView::Helpers::TranslationHelper
include Rails.application.routes.url_helpers

ISSUED_AT_LEEWAY_SECONDS = 10.seconds.to_i.freeze
ISSUED_AT_LEEWAY_SECONDS = 10

ATTRS = %i[
client_assertion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('DocumentCaptureTroubleshootingOptions', () => {
'idv.troubleshooting.options.supported_documentslinks.new_tab',
);
expect(links[1].getAttribute('href')).to.equal(
'https://example.com/redirect/?category=verify-your-identity&article=accepted-state-issued-identification&location=document_capture_troubleshooting_options',
'https://example.com/redirect/?category=verify-your-identity&article=accepted-identification-documents&location=document_capture_troubleshooting_options',
);
expect(links[1].target).to.equal('_blank');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function DocumentCaptureTroubleshootingOptions({
showDocumentTips && {
url: getHelpCenterURL({
category: 'verify-your-identity',
article: 'accepted-state-issued-identification',
article: 'accepted-identification-documents',
location,
}),
text: t('idv.troubleshooting.options.supported_documents'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,59 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
const { trackSubmitEvent, trackVisitEvent } = useContext(AnalyticsContext);
const { inPersonFullAddressEntryEnabled, inPersonURL, skipDocAuth, skipDocAuthFromHandoff } =
useContext(InPersonContext);
const appName = getConfigValue('appName');

useDidUpdateEffect(onStepChange, [stepName]);
useEffect(() => {
if (stepName) {
trackVisitEvent(stepName);
}
}, [stepName]);
const appName = getConfigValue('appName');
const inPersonLocationPostOfficeSearchForm = inPersonFullAddressEntryEnabled
? InPersonLocationFullAddressEntryPostOfficeSearchStep
: InPersonLocationPostOfficeSearchStep;

// Define different states to be used in human readable array declaration
const documentFormStep: FormStep = {
name: 'documents',
form: DocumentsStep,
title: t('doc_auth.headings.document_capture'),
};
const reviewFormStep: FormStep = {
name: 'review',
form:
submissionError instanceof UploadFormEntriesError
? withProps({
remainingSubmitAttempts: submissionError.remainingSubmitAttempts,
isResultCodeInvalid: submissionError.isResultCodeInvalid,
isFailedResult: submissionError.isFailedResult,
isFailedDocType: submissionError.isFailedDocType,
isFailedSelfie: submissionError.isFailedSelfie,
isFailedSelfieLivenessOrQuality:
submissionError.selfieNotLive || submissionError.selfieNotGoodQuality,
captureHints: submissionError.hints,
pii: submissionError.pii,
failedImageFingerprints: submissionError.failed_image_fingerprints,
})(ReviewIssuesStep)
: ReviewIssuesStep,
title: t('doc_auth.errors.rate_limited_heading'),
};

// In Person Steps
const prepareFormStep: FormStep = {
name: 'prepare',
form: InPersonPrepareStep,
title: t('in_person_proofing.headings.prepare'),
};
const locationFormStep: FormStep = {
name: 'location',
form: inPersonLocationPostOfficeSearchForm,
title: t('in_person_proofing.headings.po_search.location'),
};
const hybridFormStep: FormStep = {
name: 'switch_back',
form: InPersonSwitchBackStep,
title: t('in_person_proofing.headings.switch_back'),
};

/**
* Clears error state and sets form values for submission.
Expand Down Expand Up @@ -80,62 +125,16 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
initialValues = formValues;
}

const inPersonLocationPostOfficeSearchForm = inPersonFullAddressEntryEnabled
? InPersonLocationFullAddressEntryPostOfficeSearchStep
: InPersonLocationPostOfficeSearchStep;

const inPersonSteps: FormStep[] =
inPersonURL === undefined
? []
: ([
{
name: 'prepare',
form: InPersonPrepareStep,
title: t('in_person_proofing.headings.prepare'),
},
{
name: 'location',
form: inPersonLocationPostOfficeSearchForm,
title: t('in_person_proofing.headings.po_search.location'),
},
flowPath === 'hybrid' && {
name: 'switch_back',
form: InPersonSwitchBackStep,
title: t('in_person_proofing.headings.switch_back'),
},
].filter(Boolean) as FormStep[]);
: ([prepareFormStep, locationFormStep, flowPath === 'hybrid' && hybridFormStep].filter(
Boolean,
) as FormStep[]);

const defaultSteps: FormStep[] = submissionError
? (
[
{
name: 'review',
form:
submissionError instanceof UploadFormEntriesError
? withProps({
remainingSubmitAttempts: submissionError.remainingSubmitAttempts,
isResultCodeInvalid: submissionError.isResultCodeInvalid,
isFailedResult: submissionError.isFailedResult,
isFailedDocType: submissionError.isFailedDocType,
isFailedSelfie: submissionError.isFailedSelfie,
isFailedSelfieLivenessOrQuality:
submissionError.selfieNotLive || submissionError.selfieNotGoodQuality,
captureHints: submissionError.hints,
pii: submissionError.pii,
failedImageFingerprints: submissionError.failed_image_fingerprints,
})(ReviewIssuesStep)
: ReviewIssuesStep,
title: t('doc_auth.errors.rate_limited_heading'),
},
] as FormStep[]
).concat(inPersonSteps)
: ([
{
name: 'documents',
form: DocumentsStep,
title: t('doc_auth.headings.document_capture'),
},
].filter(Boolean) as FormStep[]);
? ([reviewFormStep] as FormStep[]).concat(inPersonSteps)
: ([documentFormStep] as FormStep[]);

// If the user got here by opting-in to in-person proofing, when skipDocAuth === true,
// then set steps to inPersonSteps
Expand Down
10 changes: 6 additions & 4 deletions app/jobs/get_usps_proofing_results_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,11 @@ def check_enrollment(enrollment)
enrollment.update(status_check_attempted_at: status_check_attempted_at)
end

def passed_with_unsupported_secondary_id_type?(response)
return response['secondaryIdType'].present? &&
SUPPORTED_SECONDARY_ID_TYPES.exclude?(response['secondaryIdType'])
def passed_with_unsupported_secondary_id_type?(enrollment, response)
return false if enrollment.enhanced_ipp?

response['secondaryIdType'].present? &&
SUPPORTED_SECONDARY_ID_TYPES.exclude?(response['secondaryIdType'])
end

def analytics(user: AnonymousUser.new)
Expand Down Expand Up @@ -483,7 +485,7 @@ def process_enrollment_response(enrollment, response)
when IPP_STATUS_PASSED
if fraud_result_pending?(enrollment)
handle_passed_with_fraud_review_pending(enrollment, response)
elsif passed_with_unsupported_secondary_id_type?(response)
elsif passed_with_unsupported_secondary_id_type?(enrollment, response)
handle_unsupported_secondary_id(enrollment, response)
elsif passed_with_primary_id_check?(enrollment, response)
handle_successful_status_update(enrollment, response)
Expand Down
4 changes: 2 additions & 2 deletions app/models/document_capture_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def store_result_from_response(doc_auth_response)
session_result.selfie_status = doc_auth_response.selfie_status
EncryptedRedisStructStorage.store(
session_result,
expires_in: IdentityConfig.store.doc_capture_request_valid_for_minutes.minutes.seconds.to_i,
expires_in: IdentityConfig.store.doc_capture_request_valid_for_minutes.minutes.in_seconds,
)
self.ocr_confirmation_pending = doc_auth_response.attention_with_barcode?
save!
Expand All @@ -45,7 +45,7 @@ def store_failed_auth_data(front_image_fingerprint:, back_image_fingerprint:,

EncryptedRedisStructStorage.store(
session_result,
expires_in: IdentityConfig.store.doc_capture_request_valid_for_minutes.minutes.seconds.to_i,
expires_in: IdentityConfig.store.doc_capture_request_valid_for_minutes.minutes.in_seconds,
)
save!
end
Expand Down
10 changes: 10 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ def edit_password_visit
# @param [String] bad_password_count represents number of prior login failures
# @param [Boolean] sp_request_url_present if was an SP request URL in the session
# @param [Boolean] remember_device if the remember device cookie was present
# @param [Boolean, nil] new_device Whether the user is authenticating from a new device. Nil if
# there is the attempt was unsuccessful, since it cannot be known whether it's a new device.
# Tracks authentication attempts at the email/password screen
def email_and_password_auth(
success:,
Expand All @@ -413,6 +415,7 @@ def email_and_password_auth(
bad_password_count:,
sp_request_url_present:,
remember_device:,
new_device:,
**extra
)
track_event(
Expand All @@ -424,6 +427,7 @@ def email_and_password_auth(
bad_password_count:,
sp_request_url_present:,
remember_device:,
new_device:,
**extra,
)
end
Expand Down Expand Up @@ -5814,6 +5818,8 @@ def user_registration_cancellation(request_came_from:, **extra)
# @param [Array] sp_session_requested_attributes Attributes requested by the service provider
# @param [Boolean] in_account_creation_flow Whether user is going through account creation flow
# @param [String, nil] disposable_email_domain Disposable email domain used for registration
# @param [String, nil] in_person_proofing_status In person proofing status
# @param [String, nil] doc_auth_result The doc auth result
def user_registration_complete(
ial2:,
service_provider_name:,
Expand All @@ -5823,6 +5829,8 @@ def user_registration_complete(
sp_session_requested_attributes:,
ialmax: nil,
disposable_email_domain: nil,
in_person_proofing_status: nil,
doc_auth_result: nil,
**extra
)
track_event(
Expand All @@ -5835,6 +5843,8 @@ def user_registration_complete(
needs_completion_screen_reason:,
sp_session_requested_attributes:,
disposable_email_domain:,
in_person_proofing_status:,
doc_auth_result:,
**extra,
)
end
Expand Down
1 change: 0 additions & 1 deletion app/services/marketing_site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ class UnknownArticleException < StandardError; end
manage-your-account/personal-key
trouble-signing-in/face-or-touch-unlock
verify-your-identity/accepted-identification-documents
verify-your-identity/accepted-state-issued-identification
verify-your-identity/how-to-add-images-of-your-state-issued-id
verify-your-identity/verify-your-identity-in-person
verify-your-identity/phone-number
Expand Down
5 changes: 2 additions & 3 deletions app/services/rate_limiter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def increment!
multi.incr(key)
multi.expireat(
key,
now + RateLimiter.attempt_window_in_minutes(rate_limit_type).minutes.seconds.to_i,
now + RateLimiter.attempt_window_in_minutes(rate_limit_type).minutes.in_seconds,
)
end
end
Expand Down Expand Up @@ -132,8 +132,7 @@ def increment_to_limited!
client.set(
key,
value,
exat: now.to_i +
RateLimiter.attempt_window_in_minutes(rate_limit_type).minutes.seconds.to_i,
exat: now.to_i + RateLimiter.attempt_window_in_minutes(rate_limit_type).minutes.in_seconds,
)
end

Expand Down
2 changes: 1 addition & 1 deletion app/services/service_provider_request_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def self.write(obj, uuid)
REDIS_POOL.with do |client|
client.setex(
key(uuid),
IdentityConfig.store.service_provider_request_ttl_hours.hours.to_i,
IdentityConfig.store.service_provider_request_ttl_hours.hours.in_seconds,
obj.to_json,
)
end
Expand Down
6 changes: 6 additions & 0 deletions app/services/usps_in_person_proofing/mock/fixtures.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ def self.request_passed_proofing_supported_secondary_id_type_results_response
)
end

def self.request_passed_proofing_secondary_id_type_results_response_ial_2
load_response_fixture(
'request_passed_proofing_secondary_id_type_results_response_ial_2.json',
)
end

def self.request_expired_proofing_results_response
load_response_fixture('request_expired_proofing_results_response.json')
end
Expand Down
Loading