Skip to content
Closed
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
24 changes: 23 additions & 1 deletion app/controllers/idv/document_capture_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class DocumentCaptureController < ApplicationController
include StepIndicatorConcern

before_action :confirm_not_rate_limited, except: [:update]
before_action :confirm_step_allowed
before_action :confirm_step_allowed, unless: -> { allow_direct_ipp? }
before_action :override_csp_to_allow_acuant

def show
Expand Down Expand Up @@ -47,6 +47,7 @@ def extra_view_variables
sp_name: decorated_sp_session.sp_name,
failure_to_proof_url: return_to_sp_failure_to_proof_url(step: 'document_capture'),
skip_doc_auth: idv_session.skip_doc_auth,
skip_doc_auth_from_handoff: idv_session.skip_doc_auth_from_handoff,
opted_in_to_in_person_proofing: idv_session.opted_in_to_in_person_proofing,
doc_auth_selfie_capture: decorated_sp_session.selfie_required?,
}.merge(
Expand All @@ -62,6 +63,7 @@ def self.step_info
preconditions: ->(idv_session:, user:) {
idv_session.flow_path == 'standard' && (
# mobile
idv_session.skip_doc_auth_from_handoff ||
idv_session.skip_hybrid_handoff ||
idv_session.skip_doc_auth ||
!idv_session.selfie_check_required || # desktop but selfie not required
Expand Down Expand Up @@ -109,5 +111,25 @@ def handle_stored_result
failure(I18n.t('doc_auth.errors.general.network_error'), extra)
end
end

def allow_direct_ipp?
# not allowed when no step param and action:show(get request)
return false if params[:step].blank? || params[:action].to_s != 'show' ||
idv_session.flow_path == 'hybrid'
# Only allow direct access to document capture if IPP available
return false unless IdentityConfig.store.in_person_doc_auth_button_enabled &&
Idv::InPersonConfig.enabled_for_issuer?(decorated_sp_session.sp_issuer)
case params[:step]
when 'hybrid_handoff'
@previous_step_url = idv_hybrid_handoff_path
else
@previous_step_url = nil
end
# allow
idv_session.flow_path = 'standard'
idv_session.skip_doc_auth_from_handoff = true
idv_session.skip_hybrid_handoff = nil
true
end
end
end
11 changes: 9 additions & 2 deletions app/controllers/idv/hybrid_handoff_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ def show
@upload_disabled = idv_session.selfie_check_required &&
!idv_session.desktop_selfie_test_mode_enabled?

@opt_in_ipp_enabled = IdentityConfig.store.in_person_doc_auth_button_enabled &&
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.

This variable name is confusing to me. The code in the selected_remote method is checking config values to determine if opt in IPP is enabled, however we are checking a different set of config values here. So I'm not totally sure I understand what this var is checking for? I'm also unclear on how it's being used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JackRyan1989 , yes it is a "general" opt in for IPP and may drift away the existing "Optin" meaning. I will rename it.

Idv::InPersonConfig.enabled_for_issuer?(decorated_sp_session.sp_issuer)

@selfie_required = idv_session.selfie_check_required

analytics.idv_doc_auth_hybrid_handoff_visited(**analytics_arguments)
Expand All @@ -22,6 +25,8 @@ def show
true
)

# reset if we visit or come back
idv_session.skip_doc_auth_from_handoff = nil
render :show, locals: extra_view_variables
end

Expand All @@ -42,9 +47,11 @@ def self.selected_remote(idv_session:)
if IdentityConfig.store.in_person_proofing_opt_in_enabled &&
IdentityConfig.store.in_person_proofing_enabled &&
idv_session.service_provider&.in_person_proofing_enabled
idv_session.skip_doc_auth == false
idv_session.skip_doc_auth == false ||
idv_session.skip_doc_auth_from_handoff == true
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.

Why are we using this value as a precondition when it's not set to true until the document capture page?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JackRyan1989 , it's for the "back" button to work from document capture(IPP) page.

So the user can go from handoff (select ipp)-> document catpure -> Back -> handoff.

This is a new flag to control this behavior similar to skip_doc_auth.

else
idv_session.skip_doc_auth.nil? || idv_session.skip_doc_auth == false
idv_session.skip_doc_auth.nil? || idv_session.skip_doc_auth == false ||
idv_session.skip_doc_auth_from_handoff == true
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
const { t } = useI18n();
const { flowPath } = useContext(UploadContext);
const { trackSubmitEvent, trackVisitEvent } = useContext(AnalyticsContext);
const { inPersonFullAddressEntryEnabled, inPersonURL, skipDocAuth } = useContext(InPersonContext);
const { inPersonFullAddressEntryEnabled, inPersonURL, skipDocAuth, skipDocAuthFromHandoff } =
useContext(InPersonContext);
const appName = getConfigValue('appName');

useDidUpdateEffect(onStepChange, [stepName]);
Expand Down Expand Up @@ -137,12 +138,15 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {

// If the user got here by opting-in to in-person proofing, when skipDocAuth === true,
// then set steps to inPersonSteps
const steps: FormStep[] = skipDocAuth ? inPersonSteps : defaultSteps;
const steps: FormStep[] = skipDocAuth || skipDocAuthFromHandoff ? inPersonSteps : defaultSteps;

// If the user got here by opting-in to in-person proofing, when skipDocAuth === true,
// If the user got here by opting-in to in-person proofing, when skipDocAuth === true;
// or opting-in ipp from handoff page, and selfie is required, when skipDocAuthFromHandoff === true
// then set stepIndicatorPath to VerifyFlowPath.IN_PERSON
const stepIndicatorPath =
(stepName && ['location', 'prepare', 'switch_back'].includes(stepName)) || skipDocAuth
(stepName && ['location', 'prepare', 'switch_back'].includes(stepName)) ||
skipDocAuth ||
skipDocAuthFromHandoff
? VerifyFlowPath.IN_PERSON
: VerifyFlowPath.DEFAULT;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ function InPersonPrepareStep({ toPreviousStep }) {
inPersonOutageMessageEnabled,
inPersonOutageExpectedUpdateDate,
skipDocAuth,
skipDocAuthFromHandoff,
howToVerifyURL,
previousStepURL,
} = useContext(InPersonContext);

function goBack() {
if (skipDocAuth && howToVerifyURL) {
if (skipDocAuthFromHandoff && previousStepURL) {
// directly from handoff page
forceRedirect(previousStepURL);
} else if (skipDocAuth && howToVerifyURL) {
forceRedirect(howToVerifyURL);
} else {
toPreviousStep();
Expand Down
11 changes: 11 additions & 0 deletions app/javascript/packages/document-capture/context/in-person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,21 @@ export interface InPersonContextProps {
*/
skipDocAuth?: boolean;

/**
* Flag set when user select IPP from handoff page when IPP is available
* and selfie is required
*/
skipDocAuthFromHandoff?: boolean;

/**
* URL for Opt-in IPP, used when in_person_proofing_opt_in_enabled is enabled
*/
howToVerifyURL?: string;

/**
* URL for going back to previous steps in Doc Auth, like handoff and howToVerify
*/
previousStepURL?: string;
}

const InPersonContext = createContext<InPersonContextProps>({
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/packs/document-capture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ interface AppRootData {
optedInToInPersonProofing: string;
securityAndPrivacyHowItWorksUrl: string;
skipDocAuth: string;
skipDocAuthFromHandoff: string;
howToVerifyURL: string;
previousStepUrl: string;
uiExitQuestionSectionEnabled: string;
docAuthSelfieDesktopTestMode: string;
}
Expand Down Expand Up @@ -105,7 +107,9 @@ const {
optedInToInPersonProofing,
usStatesTerritories = '',
skipDocAuth,
skipDocAuthFromHandoff,
howToVerifyUrl,
previousStepUrl,
uiExitQuestionSectionEnabled = '',
docAuthSelfieDesktopTestMode,
} = appRoot.dataset as DOMStringMap & AppRootData;
Expand All @@ -131,7 +135,9 @@ const App = composeComponents(
optedInToInPersonProofing: optedInToInPersonProofing === 'true',
usStatesTerritories: parsedUsStatesTerritories,
skipDocAuth: skipDocAuth === 'true',
skipDocAuthFromHandoff: skipDocAuthFromHandoff === 'true',
howToVerifyURL: howToVerifyUrl,
previousStepURL: previousStepUrl,
},
},
],
Expand Down
1 change: 1 addition & 0 deletions app/services/idv/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Session
selfie_check_performed
selfie_check_required
skip_doc_auth
skip_doc_auth_from_handoff
skip_hybrid_handoff
ssn
threatmetrix_review_status
Expand Down
1 change: 1 addition & 0 deletions app/views/idv/document_capture/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
acuant_version: acuant_version,
opted_in_to_in_person_proofing: opted_in_to_in_person_proofing,
skip_doc_auth: skip_doc_auth,
skip_doc_auth_from_handoff: skip_doc_auth_from_handoff,
doc_auth_selfie_capture: doc_auth_selfie_capture,
) %>
10 changes: 10 additions & 0 deletions app/views/idv/hybrid_handoff/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@
<%= f.submit t('forms.buttons.send_link') %>
<% end %>
</div>
<% if @opt_in_ipp_enabled %>
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.

It seems like this value is referring to a new page for document capture rather than opt in IPP, so I think we should rename it.

<div class="margin-top-4 padding-top-2 border-top border-primary-light">
<p class="margin-top-1">
<%= t('doc_auth.info.hybrid_handoff_ipp_html') %>'
</p>
<p class="margin-top-1 margin-bottom-0">
<%= link_to t('in_person_proofing.headings.prepare'), idv_document_capture_path(step: :hybrid_handoff) %>
</p>
</div>
<% end %>
</div>
<% end %>

Expand Down
2 changes: 2 additions & 0 deletions app/views/idv/shared/_document_capture.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
doc_auth_selfie_capture: FeatureManagement.idv_allow_selfie_check? && doc_auth_selfie_capture,
doc_auth_selfie_desktop_test_mode: IdentityConfig.store.doc_auth_selfie_desktop_test_mode,
skip_doc_auth: skip_doc_auth,
skip_doc_auth_from_handoff: skip_doc_auth_from_handoff,
how_to_verify_url: idv_how_to_verify_url,
previous_step_url: @previous_step_url,
ui_exit_question_section_enabled: IdentityConfig.store.doc_auth_exit_question_section_enabled,
} %>
<%= simple_form_for(
Expand Down
2 changes: 2 additions & 0 deletions config/locales/doc_auth/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ en:
at a participating Post Office
how_to_verify_troubleshooting_options_header: Want to learn more about how to verify your identity?
hybrid_handoff: We’ll collect information about you by reading your state‑issued ID.
hybrid_handoff_ipp_html: <strong>Don’t have a mobile phone?</strong> You can
verify your identity at a United States Post Office instead.
image_loaded: Image loaded
image_loading: Image loading
image_updated: Image updated
Expand Down
3 changes: 3 additions & 0 deletions config/locales/doc_auth/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ es:
how_to_verify_troubleshooting_options_header: ¿Quiere saber más sobre cómo verificar su identidad?
hybrid_handoff: Recopilaremos información sobre usted leyendo su documento de
identidad expedido por el estado.
hybrid_handoff_ipp_html: <strong>¿No tiene celular?</strong> Como alternativa,
puede verificar su identidad en una oficina de correos de Estados
Unidos.
image_loaded: Imagen cargada
image_loading: Cargando la imagen
image_updated: Imagen actualizada
Expand Down
3 changes: 3 additions & 0 deletions config/locales/doc_auth/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ fr:
how_to_verify_troubleshooting_options_header: Vous voulez en savoir plus sur la façon de vérifier votre identité?
hybrid_handoff: Nous recueillons des informations sur vous en lisant votre carte
d’identité délivrée par l’État.
hybrid_handoff_ipp_html: <strong>Vous n’avez pas de téléphone
cellulaire?</strong> Vous pouvez vérifier votre identité dans un bureau
de poste américain.
image_loaded: Image chargée
image_loading: Chargement de l’image
image_updated: Image mise à jour
Expand Down
24 changes: 24 additions & 0 deletions spec/controllers/idv/document_capture_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@
before do
allow(IdentityConfig.store).to receive(:in_person_proofing_enabled) { true }
allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled) { true }
allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).and_return(true)
allow(IdentityConfig.store).to receive(:in_person_doc_auth_button_enabled).and_return(true)
end

it 'renders show when flow path is standard' do
Expand All @@ -249,6 +251,28 @@

expect(response).to redirect_to(idv_hybrid_handoff_url)
end

it 'renders show when accessed from handoff' do
allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).and_return(true)
allow(IdentityConfig.store).to receive(:in_person_doc_auth_button_enabled).and_return(true)
get :show, params: { step: 'hybrid_handoff' }
expect(response).to render_template :show
expect(subject.idv_session.skip_doc_auth_from_handoff).to eq(true)
end
end

context 'ipp disabled for sp' do
before do
allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).and_return(false)
allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).with(anything).and_return(false)
allow(subject.decorated_sp_session).to receive(:selfie_required?).and_return(true)
end
it 'redirect back when accessed from handoff' do
subject.idv_session.skip_hybrid_handoff = nil
get :show, params: { step: 'hybrid_handoff' }
expect(response).to redirect_to(idv_hybrid_handoff_url)
expect(subject.idv_session.skip_doc_auth_from_handoff).to_not eq(true)
end
end
end

Expand Down
1 change: 1 addition & 0 deletions spec/controllers/idv/hybrid_handoff_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
allow(IdentityConfig.store).to receive(:doc_auth_selfie_desktop_test_mode).
and_return(false)
subject.idv_session.skip_doc_auth = true
subject.idv_session.skip_hybrid_handoff = true
end

it 'redirects to the how to verify page' do
Expand Down
31 changes: 31 additions & 0 deletions spec/features/idv/doc_auth/document_capture_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,37 @@
expect(page).to have_current_path(idv_phone_url)
end
end

context 'when ipp is enabled' do
let(:in_person_doc_auth_button_enabled) { true }
let(:sp_ipp_enabled) { true }
before do
allow(IdentityConfig.store).to receive(:in_person_doc_auth_button_enabled).
and_return(in_person_doc_auth_button_enabled)
allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).with(anything).
and_return(sp_ipp_enabled)
end
describe 'when ipp is selected' do
it 'proceed to the next page and start ipp' do
perform_in_browser(:desktop) do
visit_idp_from_oidc_sp_with_ial2(biometric_comparison_required: true)
sign_in_and_2fa_user(user)
complete_doc_auth_steps_before_hybrid_handoff_step
# we still have option to continue on handoff, since it's desktop no skip_hand_off
expect(page).to have_current_path(idv_hybrid_handoff_path)
expect(page).to have_content(t('doc_auth.headings.hybrid_handoff_selfie'))
click_on t('in_person_proofing.headings.prepare')
expect(page).to have_current_path(
idv_document_capture_path({ step: 'hybrid_handoff' }),
)
expect_step_indicator_current_step(
t('step_indicator.flows.idv.find_a_post_office'),
)
expect_doc_capture_page_header(t('in_person_proofing.headings.prepare'))
end
end
end
end
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions spec/features/idv/doc_auth/how_to_verify_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@
when the sp has opted into ipp' do
context 'opt in false at start but true during navigation' do
it 'should be bounced back from Hybrid Handoff to How to Verify' do
sleep(5)
expect(page).to have_current_path(idv_hybrid_handoff_url)
allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled) { true }
sleep(5)
page.refresh
expect(page).to have_current_path(idv_how_to_verify_url)
end
Expand Down
34 changes: 34 additions & 0 deletions spec/features/idv/doc_auth/hybrid_handoff_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,40 @@
expect(mobile_form).to have_name(t('forms.buttons.send_link'))
expect(page).to have_selector('h1', text: t('doc_auth.headings.hybrid_handoff_selfie'))
end
context 'on a desktop choose ipp', js: true do
let(:in_person_doc_auth_button_enabled) { true }
let(:sp_ipp_enabled) { true }
before do
allow(IdentityConfig.store).to receive(:in_person_doc_auth_button_enabled).
and_return(in_person_doc_auth_button_enabled)
allow(Idv::InPersonConfig).to receive(:enabled_for_issuer?).with(anything).
and_return(sp_ipp_enabled)
complete_doc_auth_steps_before_hybrid_handoff_step
end

context 'when ipp is enabled' do
it 'proceeds to ipp if selected and can go back' do
expect(page).to have_content(strip_tags(t('doc_auth.info.hybrid_handoff_ipp_html')))
click_on t('in_person_proofing.headings.prepare')
expect(page).to have_current_path(idv_document_capture_path({ step: 'hybrid_handoff' }))
click_on t('forms.buttons.back')
expect(page).to have_current_path(idv_hybrid_handoff_path)
end
end

context 'when ipp is disabled' do
let(:in_person_doc_auth_button_enabled) { false }
let(:sp_ipp_enabled) { false }
it 'has no ipp option can be selected' do
expect(page).to_not have_content(
strip_tags(t('doc_auth.info.hybrid_handoff_ipp_html')),
)
expect(page).to_not have_content(
t('in_person_proofing.headings.prepare'),
)
end
end
end
end

describe 'when selfie is not required by sp' do
Expand Down
Loading