LG-13963: 👤 Socure shadow mode background job#11139
Conversation
app/jobs/resolution_proofing_job.rb
Outdated
There was a problem hiding this comment.
More to do here around including phone number (which we don't currently pass to ResolutionProofingJob). Captured as LG-14282
There was a problem hiding this comment.
did we consider enqueuing this job in parallel from inside IDV agent?
There was a problem hiding this comment.
This job depends on the resolution proofing job completing before it can run (since it reads results from the proofing doc capture session). So parallel wouldn't work here
There was a problem hiding this comment.
Is that so we can compare the results easily in Cloudwatch? I feel like we would be able to accomplish that with a coalesce() | stats ... by properties.user_id or maybe we come up with a job id to join them by that's shared?
There was a problem hiding this comment.
Ooh, yeah, I like the job id idea, lemme noodle on that
There was a problem hiding this comment.
There is maybe an argument for using trace_id as a value that can connect the two jobs... It gets passed into ResolutionProofingJob, but then the IdV: doc auth verify proofing results event gets logged by the verify info concern as part of a different request, and so will have a different trace id.
So then I thought maybe create a new socure_shadow_mode_job_id, but that gets a little awkward because we have to store it on the DocumentCaptureSession so that the verify info concern can include it in the payload sent to verify proofing results so that we can then link those events with the socure events.
I don't think it's good enough to use user_id as a key here, since a single user can have multiple submissions.
Which brings me back to the solution I'm using here: schedule the job after the resolution proofing job has run, log payloads for existing + socure in a new event.
The intention here is to run this for a short period of time to gather data, so one advantage to this approach is that the change is mostly contained to the SocureShadowModeProofingJob class, with a little tweak to ResolutionProofingJob.
(I did just create LG-14328 to try addressing some of the awkwardness caused by logging our event from a controller rather than a background job)
There was a problem hiding this comment.
ok! I was thinking this, but for all the reasons you stated this seems fine as-is too
job_id = SecureRandom.uuid
ResolutionProofingJob.perform_later(job_id:, ...)
SocureShadowModeProofingJob.perform_later(job_id:, ...)There was a problem hiding this comment.
These are copied from where we log the Verify proofing results event. Socure results don't include PII so we should be good.
There was a problem hiding this comment.
We did not previously support not_to have_logged_event -- that's what the update to HaveLoggedEventMatcher (below) is about.
There was a problem hiding this comment.
I realized that the proofing result should still be available assuming the SocureShadowModeProofingJob runs soon after the ResolutionProofingJob, so it makes more sense to load the result from there rather than pass it in (which I'd been thinking when I wrote the ticket).
If jobs get delayed and this result is occasionally not available that's not a big deal.
n1zyy
left a comment
There was a problem hiding this comment.
I have a few nits and questions, and see that Zach added some as well, but overall this is looking good. Only commenting for the moment as I haven't tested this yet, but no real issues.
app/jobs/resolution_proofing_job.rb
Outdated
There was a problem hiding this comment.
I got distracted wondering if we needed to worry about getting a nil in here somewhere. It looks like there's a lot of prior art for not needing it, though.
That, in turn, led to me wondering if this should actually be implemented on User as, e.g., user.first_email_address or something.
There was a problem hiding this comment.
yeah user.email is a deprecated method but having user.first_email_address would be very clear
Add job to make requests to Socure's KYC API and log the results alongside the original resolution proofing result. changelog: Upcoming Features, Identity verification, Add background job for Socure KYC proofing
- When flag is enabled, invoke Socure KYC as well
These are real big and mess with Cloudwatch's ability to parse fields out oflogs.
Co-authored-by: Zach Margolis <zachmargolis@users.noreply.github.com>
5274082 to
181ca80
Compare
eileen-nava
left a comment
There was a problem hiding this comment.
Thanks for giving Joy a heads-up about this PR. Looks great. 👏🏻
| socure_result = proofer.proof(applicant) | ||
|
|
||
| analytics.idv_socure_shadow_mode_proofing_result( | ||
| resolution_result: format_proofing_result_for_logs(proofing_result), |
There was a problem hiding this comment.
Non-blocking nit: My read is that this method removes the threatmetrix response body. Would it be worth indicating that in the method name?
(Previously: #11093)
🎫 Ticket
Link to the relevant ticket:
LG-13963
🛠 Summary of changes
Adds a new disabled background job,
SocureShadowModeProofingJob, to do proofing with Socure KYC and log the result.To enable the job, you need to set
idv_socure_shadow_mode: truein your config. Then it will be scheduled automatically by the ResolutionProofingJob.📜 Testing Plan
To test this locally, you will need a Socure KYC API key. To get one:
Then update your
application.yml:Then, complete identity verification. DO NOT USE REAL PII. You should see a new event logged in your events.log called
idv_socure_shadow_mode_proofing_result:Here is an example
{ "name": "idv_socure_shadow_mode_proofing_result", "properties": { "event_properties": { "resolution_result": { "success": true, "errors": {}, "exception": null, "timed_out": false, "threatmetrix_review_status": "pass", "context": { "device_profiling_adjudication_reason": "device_profiling_result_pass", "resolution_adjudication_reason": "pass_resolution_and_state_id", "should_proof_state_id": true, "stages": { "resolution": { "success": true, "errors": {}, "exception": null, "timed_out": false, "transaction_id": "resolution-mock-transaction-id-123", "reference": "aaa-bbb-ccc", "can_pass_with_additional_verification": false, "attributes_requiring_additional_verification": [], "vendor_name": "ResolutionMock", "vendor_workflow": null, "verified_attributes": null }, "residential_address": { "success": true, "errors": {}, "exception": null, "timed_out": false, "transaction_id": "", "reference": "", "can_pass_with_additional_verification": false, "attributes_requiring_additional_verification": [], "vendor_name": "ResidentialAddressNotRequired", "vendor_workflow": null, "verified_attributes": null }, "state_id": { "success": true, "errors": {}, "exception": null, "mva_exception": null, "requested_attributes": {}, "timed_out": false, "transaction_id": "state-id-mock-transaction-id-456", "vendor_name": "StateIdMock", "verified_attributes": [] }, "threatmetrix": { "client": null, "success": true, "errors": {}, "exception": null, "timed_out": false, "transaction_id": "ddp-mock-transaction-id-123", "review_status": "pass" } } }, "ssn_is_unique": true }, "socure_result": { "success": true, "errors": { "reason_codes": [ "I919" ] }, "exception": null, "timed_out": false, "transaction_id": "4fc4b744-79d0-4df7-8751-e087609fb1fa", "reference": "", "can_pass_with_additional_verification": false, "attributes_requiring_additional_verification": [], "vendor_name": "socure_kyc", "vendor_workflow": null, "verified_attributes": [ "address", "first_name", "last_name", "ssn", "dob" ] } }, "new_event": true, "path": null, "session_duration": null, "user_id": "96d74912-6af8-4106-8d93-7485f04a3137", "locale": "en" }, "time": "2024-08-22T19:25:45.678Z", "id": "d367752a-707e-4708-8ac1-781167163b0c", "visitor_id": "0b2d3739-287f-47ef-90c4-952b2fedb72e", "visit_id": "8a3f78d2-406e-4481-b839-ac18c03c9fda", "log_filename": "events.log" }You should also verify that this job does not run when
idv_socure_shadow_mode_enabledis not set or is disabled.