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
4 changes: 3 additions & 1 deletion app/controllers/idv/capture_doc_status_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ def document_capture_session_poll_render_result
document_capture_session = DocumentCaptureSession.find_by(uuid: session_uuid)
return { plain: 'Unauthorized', status: :unauthorized } unless document_capture_session

result = document_capture_session.load_result
result = document_capture_session.load_result ||
document_capture_session.load_doc_auth_async_result
Comment on lines +19 to +20
Copy link
Copy Markdown
Contributor Author

@zachmargolis zachmargolis Feb 11, 2021

Choose a reason for hiding this comment

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

reason this is needed: the VerifyDocumentAction stores data via store_doc_auth_result which still contains PII but has slightly different redis keys than the store_result_from_response that the old flow uses


return { plain: 'Pending', status: :accepted } if result.blank?
return { plain: 'Unauthorized', status: :unauthorized } unless result.success?
{ plain: 'Complete', status: :ok }
Expand Down
2 changes: 2 additions & 0 deletions app/services/document_capture_session_async_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def done?
status == DocumentCaptureSessionAsyncResult::DONE
end

alias_method :success?, :done?
Copy link
Copy Markdown
Contributor Author

@zachmargolis zachmargolis Feb 11, 2021

Choose a reason for hiding this comment

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

reason this is needed: the capture doc flow expects to load a DocumentCaptureSessionResult which is basically the same as this one, except that it responds to success?


def in_progress?
status == DocumentCaptureSessionAsyncResult::IN_PROGRESS
end
Expand Down
10 changes: 7 additions & 3 deletions app/services/idv/actions/verify_document_action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ def form
end

def enqueue_job
verify_document_capture_session = create_document_capture_session(
verify_document_capture_session_uuid_key,
)
verify_document_capture_session = if hybrid_flow_mobile?
document_capture_session
else
create_document_capture_session(
verify_document_capture_session_uuid_key,
)
end
Comment on lines +33 to +39
Copy link
Copy Markdown
Contributor Author

@zachmargolis zachmargolis Feb 11, 2021

Choose a reason for hiding this comment

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

reason this is needed: in the hybrid mobile flow, the document_capture_session is the UUID that the desktop sent over, so that's where the desktop is polling for results. In the non-hybrid flow, we can generate a new key like this does

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.

the key is only used once either way, so we don't need to worry about overwriting?

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.

I believe, yes

verify_document_capture_session.requested_at = Time.zone.now
verify_document_capture_session.create_doc_auth_session

Expand Down
10 changes: 7 additions & 3 deletions app/services/idv/actions/verify_document_status_action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ def process_result(result)

def verify_document_capture_session
return @verify_document_capture_session if defined?(@verify_document_capture_session)
@verify_document_capture_session = DocumentCaptureSession.find_by(
uuid: flow_session[verify_document_capture_session_uuid_key],
)
@verify_document_capture_session = if hybrid_flow_mobile?
document_capture_session
else
DocumentCaptureSession.find_by(
uuid: flow_session[verify_document_capture_session_uuid_key],
)
end
end

def async_state
Expand Down
2 changes: 2 additions & 0 deletions app/services/idv/flows/capture_doc_flow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class CaptureDocFlow < Flow::BaseFlow

ACTIONS = {
reset: Idv::Actions::ResetAction,
verify_document: Idv::Actions::VerifyDocumentAction,
verify_document_status: Idv::Actions::VerifyDocumentStatusAction,
Comment on lines +11 to +12
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.

reason this is needed: these polling actions for async work were missing from the hybrid flow

}.freeze

def initialize(controller, session, _name)
Expand Down
4 changes: 4 additions & 0 deletions app/services/idv/steps/doc_auth_base_step.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ def user_id_from_token
flow_session[:doc_capture_user_id]
end

def hybrid_flow_mobile?
user_id_from_token.present?
end

def throttled_response
redirect_to throttled_url
IdentityDocAuth::Response.new(
Expand Down
5 changes: 4 additions & 1 deletion app/services/idv/steps/link_sent_step.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ def take_photo_with_phone_successful?
end

def document_capture_session_result
@document_capture_session_result ||= document_capture_session&.load_result
@document_capture_session_result ||= (
document_capture_session&.load_result ||
document_capture_session&.load_doc_auth_async_result
)
Comment on lines +34 to +37
Copy link
Copy Markdown
Contributor Author

@zachmargolis zachmargolis Feb 11, 2021

Choose a reason for hiding this comment

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

allow loading data from the async flow

end

def mark_steps_complete
Expand Down
4 changes: 2 additions & 2 deletions app/views/idv/shared/_document_capture.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
mock_client: (DocAuthRouter.doc_auth_vendor == 'mock').presence,
document_capture_session_uuid: flow_session[:document_capture_session_uuid],
endpoint: FeatureManagement.document_capture_async_uploads_enabled? ?
idv_doc_auth_step_path(step: :verify_document) :
send(@step_url, step: :verify_document) :
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.

reason this is needed:

In the non-hybrid flow, we link to /idv/doc_auth etc endpoints. However, in the hybrid flow, the mobile path hits /idv/capture_doc/ versions of the corresponding endpoints, so this needed to be updated to be dynamic based on the flow

api_verify_images_url,
status_endpoint: FeatureManagement.document_capture_async_uploads_enabled? ?
idv_doc_auth_step_path(step: :verify_document_status) :
send(@step_url, step: :verify_document_status) :
nil,
status_poll_interval_ms: AppConfig.env.poll_rate_for_verify_in_seconds.to_i * 1000,
sp_name: sp_name,
Expand Down
62 changes: 62 additions & 0 deletions spec/features/idv/hybrid_flow_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'rails_helper'

describe 'Hybrid Flow' do
include IdvHelper
include DocAuthHelper

before do
allow(FeatureManagement).to receive(:doc_capture_polling_enabled?).and_return(true)
allow(AppConfig.env).to receive(:doc_auth_enable_presigned_s3_urls).and_return('true')
allow(AppConfig.env).to receive(:document_capture_async_uploads_enabled).and_return('true')
allow(LoginGov::Hostdata::EC2).to receive(:load).
and_return(OpenStruct.new(region: 'us-west-2', account_id: '123456789'))
end

it 'proofs and hands off to mobile', js: true do
user = nil
sms_link = nil

expect(Telephony).to receive(:send_doc_auth_link).and_wrap_original do |impl, config|
sms_link = config[:link]
impl.call(config)
end

perform_in_browser(:desktop) do
user = sign_in_and_2fa_user
complete_doc_auth_steps_before_send_link_step
fill_in :doc_auth_phone, with: '415-555-0199'
click_idv_continue

expect(page).to have_content(t('doc_auth.headings.text_message'))
end

expect(sms_link).to be_present

perform_in_browser(:mobile) do
visit sms_link
attach_and_submit_images
expect(page).to have_text(t('doc_auth.instructions.switch_back'))
end

perform_in_browser(:desktop) do
expect(page).to_not have_content(t('doc_auth.headings.text_message'), wait: 10)

fill_out_ssn_form_ok
click_idv_continue

expect(page).to have_content(t('doc_auth.headings.verify'))
click_idv_continue

fill_out_phone_form_mfa_phone(user)
click_idv_continue

fill_in :user_password, with: Features::SessionHelper::VALID_PASSWORD
click_idv_continue

acknowledge_and_confirm_personal_key

expect(page).to have_current_path(account_path)
expect(page).to have_content(t('headings.account.verified_account'))
end
end
end
31 changes: 22 additions & 9 deletions spec/views/idv/shared/_document_capture.html.erb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,42 @@
let(:selfie_image_upload_url) { nil }

before do
allow(view).to receive(:flow_session).and_return(flow_session)
allow(view).to receive(:sp_name).and_return(sp_name)
allow(view).to receive(:failure_to_proof_url).and_return(failure_to_proof_url)
allow(view).to receive(:front_image_upload_url).and_return(front_image_upload_url)
allow(view).to receive(:back_image_upload_url).and_return(back_image_upload_url)
allow(view).to receive(:selfie_image_upload_url).and_return(selfie_image_upload_url)
allow(view).to receive(:url_for).and_return('https://example.com/')

allow(FeatureManagement).to receive(:document_capture_async_uploads_enabled?).
and_return(async_uploads_enabled)

assign(:step_url, :idv_doc_auth_step_url)
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.

this is the key change in this file, now that the partial uses @step_url. The rest of the changes in the file were to simplify and minimize stubbing

end

subject(:render_partial) do
render partial: 'idv/shared/document_capture', locals: {
flow_session: flow_session,
sp_name: sp_name,
failure_to_proof_url: failure_to_proof_url,
front_image_upload_url: front_image_upload_url,
back_image_upload_url: back_image_upload_url,
selfie_image_upload_url: selfie_image_upload_url,
}
end

describe 'async upload urls' do
context 'when async upload is disabled' do
let(:async_uploads_enabled) { false }

it 'does not modify CSP connect_src headers' do
allow(SecureHeaders).to receive(:append_content_security_policy_directives).with(any_args)
expect(SecureHeaders).to receive(:append_content_security_policy_directives).with(
controller.request,
connect_src: [],
)

render
render_partial
end
end

context 'when async upload is enabled' do
context 'when async upload are enabled' do
let(:async_uploads_enabled) { true }
let(:front_image_upload_url) { 'https://s3.example.com/bucket/a?X-Amz-Security-Token=UAOL2' }
let(:back_image_upload_url) { 'https://s3.example.com/bucket/b?X-Amz-Security-Token=UAOL2' }
let(:selfie_image_upload_url) { 'https://s3.example.com/bucket/c?X-Amz-Security-Token=UAOL2' }
Expand All @@ -49,7 +62,7 @@
],
)

render
render_partial
end
end
end
Expand Down