-
Notifications
You must be signed in to change notification settings - Fork 53
Identity Verification (Experian)
When SimpleReport first started, all identity verification sessions were done manually over Zoom. In August 2021, we started using Experian for automated identity verification services. Today, this automatic id verification process takes around 5 minutes to complete and successfully verifies about 50% of new users. The other 50% of users continue to go through manual verification, either because they can't be found or can't be verified.
For automated identity verification, we request personal information from users. As of 12/21/21, this includes:
- Full name
- DOB
- Home address
- Phone number
- Email address
If Experian is able to find the user, they're directed to a set of multiple choice questions. These questions ask them to confirm personal information, like what high school they went to, what bank their mortgage is with, etc. If the user successfully answers the questions and Experian has high confidence they are who they claim to be, the user passes automated identity verification and their SimpleReport organization is created. If Experian fails or has low confidence in the user's responses, the user is directed to schedule a manual verification session with our Support team.
We route identity verification requests through our backend to Experian's CrossCore API. Each request contains application-level Experian secrets, including a username
, password
, subscriberCode
, tenantId
, and clientReferenceId
. These secrets have different values in demo and production environments, set in the appropriate application.yaml
. All requests are also required to contain a separate JWT issued by Experian. Theoretically these JWTs live for 30 minutes before expiration, but at time of writing we request a JWT for each request sent to Experian. This was initially done for ease of implmentation and because we weren't receiving enough account requests to warrant persisting the token, but this may be worth revisiting.
Unlike most processes in SimpleReport, the Experian flow is anonymous - users aren't logged in through Okta when they go through identity verification. We are therefore unable to use the typical GraphQL flow and instead have a separate Controller
class, IdentityVerificationController
.
This controller class has two endpoints, get-questions
and submit-answers
.
get-questions
takes in an IdentityVerificationQuestionsRequest from the frontend. This request contains all the PII that a user has input, including name, DOB, and address. We then send this over to ExperianService
which has two versions, LiveExperianService
and DemoExperianService
. The demo version is used for local development and running tests; the live version is used for most deployed environments, though only the production environment runs with production credentials.
Experian's API consists of a series of REST endpoints that are expecting carefully formatted JSONs. We create these JSONs in ExperianTranslator
, and let's all hope Experian never changes the JSON format because untangling them will be atrocious. The questions body is created in createInitialRequestBody
, which takes both the user-input question data and the application-level Experian secrets. We reformat the user input to these JSONs, and send the request to Experian.
Experian responds with more highly structured JSONs, though we only care about a few fields in the response. First is the kbaResultCode
, which is sent in lieu of a typical HTTP status response. This is where we look to determine if the consumer wasn't found or questions couldn't be retrieved. If we are able to find questions, they're sent back to IdentityVerificationController
and eventually the frontend, along with an Experian-provided session id that links the question session to the answer session. This session id is only valid for 5 minutes; if a user takes more than 5 minutes to complete the questions they will automatically be denied by Experian.
Once the user has completed their questions, the frontend sends back the responses. It's important to note that we don't send the responses in plain text, but rather send the number of the multiple choice item selected. The list of responses, along with the user's session id, is provided to the submit-answers
endpoint using the IdentityVerificationAnswersRequest
type, which contains the organization id associated with the user, the Experian-provided session id, and the user-provided answers.
We once again format the request according to Experian's JSON types, this time using createSubmitAnswersRequestBody
and send them the request. They will respond with a JSON that includes a final decision
, which is generally one of ACCEPT or REF (for refer, or continue investigating - this basically means denied for us.) Referred users are directed to the manual verification process.
Accepted users go through the automated organization creation process, creating their org in both our database and Okta. The admin user is also instantiated in Okta and sent a welcome email that allows them to set up their login credentials and continue setting up their organization. This is done in IdentityVerificationController
, through _orgQueueService.createAndActivateQueuedOrganization
.
The two main frontend components for the Experian flow are the PersonalDetailsForm
and QuestionsForm
. PersonalDetailsForm
collects the user's personal information and sends it to the get-questions
endpoint, while QuestionsForm
displays the questions returned by Experian and calls the submit-answers
endpoint once users have finished inputting their answers.
The test
and dev
environments are set up with Experian demo credentials, allowing us to send fake user information through are service and test out different flows (user not found, user found but questions answered incorrectly, or user found and questions answered correctly.) Please contact another developer for the list of demo users, and use it sparingly - testing too often on a single demo user can have unexpected side effects, namely that the user will be denied for suspicious activity. Do NOT attempt to use your own information in the production environment to verify that things are working correctly; this violates our terms of agreement with Experian.
Debugging Experian requests is quite difficult, as we don't log any PII. This means our log statements are kept to recording when questions and answers are sent and recieved, and what the answer type is. We effectively can't get to the bottom of a given request, so if users ask "why was I denied?" there's not much we can do to help them.
There's one alerting exception for Experian, ExperianAuthException
. This is thrown if Experian doesn't recognize the credentials we're sending them, which has happened in the past when they auto-expired our application password and didn't tell us. The only immediate action to take is to see if the code or auth has changed recently, and to contact Experian if it hasn't.
These are the other common Experian exceptions.
BadRequestException
- this one is common across SimpleReport, and simply means that the user has sent a request with bad data. It could be missing critical information like name or DOB, but is largely an expected exception.
ExperianPersonMatchException
- Experian couldn't find the user. This is fairly common and isn't actionable unless it makes up the majority of our Experian traffic (50% or higher), in which case we escalate to Experian.
ExperianGetQuestionsException
- We couldn't fetch questions for the user, though not the same as the PersonMatchException
. Likely the response from Experian wasn't formatted in the way we expected.
ExperianSubmitAnswersException
- The answers couldn't be validated by Experian, though not for a common reason like denial. They sent us an unexpected response or no response at all.
ExperianKbaResultException
- Experian sent us an unexpected failure in place of the Kba node (PersonMatchException
is the expected failure in this case.)
ExperianNullNodeException
- The place we normally look in Experian's response was null.
For many configuration changes, you'll need to contact the Experian team directly. For example, we do not have the power to control what types of questions are asked or how many are asked directly, and instead have to reach out to Experian to request changes. This process frequently takes a week minimum and often longer, so please request changes well in advance when possible.
Experian documents are hosted on the CDC's private Sharepoint. Ask another developer for access.
We do not currently collect SSN when attempting to verify a user's identity. As a result, Experian is unable to find many of our users, as the information we do collect (DOB, address, name, and phone) often isn't enough to uniquely identify someone. We are regularly encouraged by Experian to ask for SSN, and at this point we do have permission from the CDC security team to do so. However, the group of users signing up for SimpleReport is generally not expecting to input sensitive data like SSN, and there's concern that asking them to do so would drive some users away from the application.
Users who abandon the Experian flow are sent a reminder email to ensure they still have a path forward for creating their organization. The ReminderService
handles this by looking at any pending organizations within the last 24 hours who haven't completed identity verification and sending them a reminder email. This helps catch users who may drop out of the flow because they balk at inputting personal information.
- Getting Started
- [Setup] Docker and docker compose development
- [Setup] IntelliJ run configurations
- [Setup] Running DB outside of Docker (optional)
- [Setup] Running nginx locally (optional)
- [Setup] Running outside of docker
- Accessing and testing weird parts of the app on local dev
- Accessing patient experience in local dev
- API Testing with Insomnia
- Cypress
- How to run e2e locally for development
- E2E tests
- Database maintenance
- MailHog
- Running tests
- SendGrid
- Setting up okta
- Sonar
- Storybook and Chromatic
- Twilio
- User roles
- Wiremock
- CSV Uploader
- Log local DB queries
- Code review and PR conventions
- SimpleReport Style Guide
- How to Review and Test Pull Requests for Dependabot
- How to Review and Test Pull Requests with Terraform Changes
- SimpleReport Deployment Process
- Adding a Developer
- Removing a developer
- Non-deterministic test tracker
- Alert Response - When You Know What is Wrong
- What to Do When You Have No Idea What is Wrong
- Main Branch Status
- Maintenance Mode
- Swapping Slots
- Monitoring
- Container Debugging
- Debugging the ReportStream Uploader
- Renew Azure Service Principal Credentials
- Releasing Changelog Locks
- Muting Alerts
- Architectural Decision Records
- Backend Stack Overview
- Frontend Overview
- Cloud Architecture
- Cloud Environments
- Database ERD
- External IDs
- GraphQL Flow
- Hibernate Lazy fetching and nested models
- Identity Verification (Experian)
- Spring Profile Management
- SR Result bulk uploader device validation logic
- Test Metadata and how we store it
- TestOrder vs TestEvent
- ReportStream Integration
- Feature Flag Setup
- FHIR Resources
- FHIR Conversions
- Okta E2E Integration
- Deploy Application Action
- Slack notifications for support escalations
- Creating a New Environment Within a Resource Group
- How to Add and Use Environment Variables in Azure
- Web Application Firewall (WAF) Troubleshooting and Maintenance
- How to Review and Test Pull Requests with Terraform Changes