diff --git a/app/controllers/api/verify/base_controller.rb b/app/controllers/api/verify/base_controller.rb index 2b54c602cf0..e99bc6f9473 100644 --- a/app/controllers/api/verify/base_controller.rb +++ b/app/controllers/api/verify/base_controller.rb @@ -1,7 +1,9 @@ module Api module Verify class BaseController < ApplicationController + skip_before_action :verify_authenticity_token include RenderConditionConcern + include EffectiveUser class_attribute :required_step @@ -11,14 +13,19 @@ class BaseController < ApplicationController end IdentityConfig.store.idv_api_enabled_steps.include?(self.class.required_step) end - before_action :confirm_two_factor_authenticated_for_api + def render_errors(error_or_errors, status: :bad_request) + errors = error_or_errors.instance_of?(Hash) ? error_or_errors : Array(error_or_errors) + render json: { errors: errors }, status: status + end + + before_action :confirm_two_factor_authenticated_for_api respond_to :json private def confirm_two_factor_authenticated_for_api - return if user_fully_authenticated? + return if effective_user render json: { error: 'user is not fully authenticated' }, status: :unauthorized end end diff --git a/app/controllers/api/verify/document_capture_controller.rb b/app/controllers/api/verify/document_capture_controller.rb new file mode 100644 index 00000000000..fdb971439f2 --- /dev/null +++ b/app/controllers/api/verify/document_capture_controller.rb @@ -0,0 +1,80 @@ +module Api + module Verify + class DocumentCaptureController < BaseController + self.required_step = 'document_capture' + include ApplicationHelper + include EffectiveUser + + def create + result = Idv::ApiDocumentVerificationForm.new( + verify_params, + liveness_checking_enabled: liveness_checking_enabled?, + analytics: analytics, + ).submit + + if result.success? + enqueue_job + + render json: { + status: 'in_progress', + }, status: :ok + else + render_errors(result.errors) + end + end + + private + + def enqueue_job + verify_document_capture_session = DocumentCaptureSession. + find_by(uuid: params[:document_capture_session_uuid]) + verify_document_capture_session.requested_at = Time.zone.now + verify_document_capture_session.create_doc_auth_session + + document_attributes = verify_params.to_h + applicant = { + user_uuid: effective_user.uuid, + uuid_prefix: current_sp&.app_id, + document_arguments: document_attributes, + } + Idv::Agent.new(applicant).proof_document( + verify_document_capture_session, + liveness_checking_enabled: liveness_checking_enabled?, + trace_id: amzn_trace_id, + image_metadata: image_metadata, + analytics_data: { + browser_attributes: analytics.browser_attributes, + }, + flow_path: params[:flow_path], + ) + nil + end + + def verify_params + params.permit( + :encryption_key, + :front_image_iv, + :back_image_iv, + :selfie_image_iv, + :front_image_url, + :back_image_url, + :selfie_image_url, + :document_capture_session_uuid, + :flow_path, + ) + end + + def image_metadata + params.permit(:front_image_metadata, :back_image_metadata). + to_h. + transform_values do |str| + JSON.parse(str, symbolize_names: true) + rescue JSON::ParserError + nil + end. + compact. + transform_keys { |key| key.gsub(/_image_metadata$/, '') } + end + end + end +end diff --git a/app/services/idv/actions/verify_document_action.rb b/app/services/idv/actions/verify_document_action.rb index c482ba9e39c..863bfde0524 100644 --- a/app/services/idv/actions/verify_document_action.rb +++ b/app/services/idv/actions/verify_document_action.rb @@ -65,8 +65,6 @@ def enqueue_job nil end - private - def image_params params.permit( ['encryption_key', 'front_image_iv', 'back_image_iv', 'selfie_image_iv', diff --git a/config/routes.rb b/config/routes.rb index ddbb23ac555..5d388ae6ac4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -339,6 +339,7 @@ namespace :api do post '/verify/v2/password_confirm' => 'verify/password_confirm#create' post '/verify/v2/password_reset' => 'verify/password_reset#create' + post '/verify/v2/document_capture' => 'verify/document_capture#create' end get '/account/verify' => 'idv/gpo_verify#index', as: :idv_gpo_verify diff --git a/spec/controllers/api/verify/document_capture_controller_spec.rb b/spec/controllers/api/verify/document_capture_controller_spec.rb new file mode 100644 index 00000000000..d3e6ab1215a --- /dev/null +++ b/spec/controllers/api/verify/document_capture_controller_spec.rb @@ -0,0 +1,101 @@ +require 'rails_helper' + +describe Api::Verify::DocumentCaptureController do + include PersonalKeyValidator + include SamlAuthHelper + + let(:encryption_key) { 'encryption-key' } + let(:front_image_url) { 'http://example.com/front' } + let(:front_image_iv) { 'front-iv' } + let(:back_image_url) { 'http://example.com/back' } + let(:back_image_iv) { 'back-iv' } + let(:selfie_image_url) { 'http://example.com/selfie' } + let(:selfie_image_iv) { 'selfie-iv' } + let(:front_image_metadata) do + { width: 40, height: 40, mimeType: 'image/png', source: 'upload' } + end + let(:back_image_metadata) do + { width: 20, height: 20, mimeType: 'image/png', source: 'upload' } + end + let(:image_metadata) { { front: front_image_metadata, back: back_image_metadata } } + let!(:document_capture_session) { DocumentCaptureSession.create!(user: create(:user)) } + let(:document_capture_session_uuid) { document_capture_session.uuid } + let(:password) { 'iambatman' } + let(:user) { create(:user, :signed_up) } + let(:flow_path) { 'standard' } + let(:liveness_checking_enabled) { false } + let(:analytics_data) { + { browser_attributes: + { browser_bot: false, + browser_device_name: 'Unknown', + browser_mobile: false, + browser_name: 'Unknown Browser', + browser_platform_name: 'Unknown', + browser_platform_version: '0', + browser_version: '0.0', + user_agent: 'Rails Testing' } } + } + + before do + allow(IdentityConfig.store).to receive(:idv_api_enabled_steps).and_return(['document_capture']) + stub_sign_in(user) + end + + describe '#create' do + context 'When user document is submitted to be verified ' do + it 'returns inprogress status when create is called' do + agent = instance_double(Idv::Agent) + allow(Idv::Agent).to receive(:new).and_return(agent) + + expect(agent).to receive(:proof_document).with( + document_capture_session, + liveness_checking_enabled: liveness_checking_enabled, + trace_id: nil, + image_metadata: image_metadata, + analytics_data: analytics_data, + flow_path: flow_path, + ) + + post :create, params: { + encryption_key: encryption_key, + front_image_iv: front_image_iv, + back_image_iv: back_image_iv, + selfie_image_iv: selfie_image_iv, + front_image_url: front_image_url, + back_image_url: back_image_url, + selfie_image_url: selfie_image_url, + front_image_metadata: front_image_metadata.to_json, + back_image_metadata: back_image_metadata.to_json, + document_capture_session_uuid: document_capture_session_uuid, + flow_path: flow_path, + } + expect(JSON.parse(response.body)['status']).to eq('in_progress') + expect(response.status).to eq 200 + end + + context 'When the request does not have all the parameters' do + it 'returns 400 and gives error message' do + agent = instance_double(Idv::Agent) + allow(Idv::Agent).to receive(:new).and_return(agent) + expect(agent).to_not receive(:proof_document) + + post :create, params: { + encryption_key: encryption_key, + front_image_iv: nil, + back_image_iv: back_image_iv, + selfie_image_iv: selfie_image_iv, + front_image_url: front_image_url, + back_image_url: back_image_url, + selfie_image_url: selfie_image_url, + document_capture_session_uuid: document_capture_session_uuid, + } + + expect(JSON.parse(response.body)['errors'].keys.first).to eq('front_image_iv') + expect(JSON.parse(response.body)['errors']['front_image_iv'][0]). + to eq('Please fill in this field.') + expect(response.status).to eq 400 + end + end + end + end +end