...
The goal of this project is to replace the current vendor for identity verification (IDV) on edx.org, Software Secure, with a new vendor, Persona. On edx.org, IDV is used solely for verifying certain name changes requested by certificate-bearing learners via the Name Affirmation feature in the frontend-app-account MFE, but IDV may be used for other purposes in the Open edX community (e.g. to block generation of a verified certificate).
...
The backend implementation of IDV is in the edx-platform verify_student Django application, and the frontend implementation of IDV and the Name Affirmation feature that uses it IDV is in the frontend-app-learning MFE . Both and frontend-app-account MFE, respectively. All three repositories are in the openedx
GitHub organization and are a part of the Open edX platform.
We would like to design propose an integration approach that balances the needs of 2U and the community. We would like to leverage propose leveraging the various extensibility mechanisms that the platform supports, but we need guidance around how “pluggable” the solution must be and what IDV-related community needs must be met, particularly since 2U uses IDV for a smaller set of features than the platform currently supports.
There are also parts of the verify_student application that have an unclear purpose and may have limited need in the community save for an operator or two. For example, the ReverifyView does not appear to be used anywhere The PayAndVerifyView also appears to have limited use. We do not have the bandwidth to do the deprecation at this time, but it would be helpful to know which parts of the codebase are core and which are unused legacy code.
Use Cases
...
As a learner who has earned a certificate, I need to be able to verify my identity after requesting a name change, because this validates that I am who I say I am. This maintains the value of a certificate and ensures that the certificate is attributable to me.
As a learner who has purchased a verifiable course mode, I need to be able to verify my identity so that I can receive a “verified certificate” when I meet the requirements for earning one.
As an Open edX installation operator, I need to be able to integrate new an IDV vendors implementation into the platform with no disruption to other Open edX instances, because I need to verify learners' identities so that they can earn verified certificates. I need to be able to integrate whichever IDV implementation that suits my needs.
Proposed Solution
Our proposed solution involves three categories of changes.
Backend Extensibility Mechanisms
We propose using the hooks extension framework to integrate additional IDV implementations into the edx-platform verify_student Django application.
Filter Hooks
We propose integrating filter hooks into the IDVerificationService. The IDVerificationService serves as an interface to the verify_student Django application and is used throughout the platform. Callers of this service primarily use it to read data from the IDV related models and to get the URL to the IDV flow.
We propose the following two filters, which will enable full integration of the IDVerificationService with additional IDV implementations.
IDVerificationAttemptsRequested(verifications, uuids, users, statuses)
This filter hook will be used to read IDV records from plugins. This will allow Open edX operators to expose supplemental IDV records to the platform via the IDVerificationService.
The uuids
, users
, and statuses
parameters are intended to be iterables of values to filter the IDV records by, corresponding to the uuid
, user.id
and status
fields of the IDVerificationAttempt model, which is the abstract base class that SoftwareSecurePhotoVerification (via PhotoVerification), SSOVerification, and ManualVerification inherit from. As a note uuid
is not currently a field on the IDVerificationAttempt model; our proposal for its addition is discussed in Refactoring.
verifications
in intended to
...
be an iterable of data class instances that represent IDV records. We propose replacing the use of model instances in the IDVerificationService with data classes. Using data classes will avoid exposing data models outside the verify_student Django application, as encouraged by OEP-49: Django App Patterns.
An app’s Django models and other internal data structures should not be exposed via its Python APIs (unless performance requires it).
Using data classes will also simplify the integration of additional IDV implementations by decoupling the verify_student data model from the data model of additional IDV implementations. Because plugins cannot and likely should not import the IDVerificationAttempt model to use as a parent class, if we continue to use model instances, any additional IDV models must replicate the database schema used by the IDVerificationAttempt model.
The IDVerificationService uses some additional database level filters (e.g. created_at__gt) that would not be supported by this filter. To avoid needing to support additional filter parameters, we propose only supporting querying by a select few fields and doing any additional querying in Python code. This will require some additional lightweight refactoring.
IDVerificationURLRequested(url)
This filter hook will be used to fetch the URL to the IDV flow. This will allow operators to override the URL to the IDV flow.
Event Hooks
We propose introducing event hooks related to the IDV attempt lifecycle. Taking the EXAM_ATTEMPT_*
Open edX events as a model, we propose introducing one Open edX event per IDV status.
IDV_ATTEMPT_CREATED
IDV_ATTEMPT_READY
IDV_ATTEMPT_SUBMITTED
IDV_ATTEMPT_MUST_RETRY
IDV_ATTEMPT_APPROVED
IDV_ATTEMPT_DENIED
It’s worth noting that the statuses of the IDVerificationAttempt model may not be entirely generic. The statuses are somewhat specific to Software Secure. As an example, they make the assumption that IDV is an asynchronous process (e.g. the distinction between ready
and submitted
). However, Persona, for example, performs IDV verifications within 5 seconds, so there is no valuable distinction between a ready
and submitted
state. These may not be the best statuses to use, but they map to the existing database states.
We could limit the set of events to more widely applicable ones.
IDV_ATTEMPT_CREATED
IDV_ATTEMPT_SUBMITTED
IDV_ATTEMPT_APPROVED
IDV_ATTEMPT_DENIED
The actual shape of the event data could be determined later.
Frontend Extensibility Mechanisms
We propose using the frontend-plugin-framework to integrate additional IDV implementations into the frontend-app-account MFE.
The current IDV flow is hosted in the frontend-app-account MFE. Although it works well, it is incompatible with Persona’s IDV flow. The Persona application collects all PII, government ID photos, and selfie photos (i.e. portrait photos) directly in the application, but edX’s current IDV flow performs all of those functions and submits the collected photos to Software Secure via the LMS.
For this reason, we need to replace the current IDV flow. In order to do this without modifying the current IDV flow hosted in the frontend-app-account MFE, we propose the addition of a PluginSlot from the frontend-plugin-framework to the frontend-app-account MFE. This PluginSlot would wrap the use of the IdVerificationPage in the id-verification
Route
. This would allow us to replace the entire IDV component with a new Persona integration component without needing to modify the route or the router.
There are a few other options for where to add a PluginSlot. These are documented in Alternative PluginSlot Locations.
Sample code is shown below for illustrative purposes.
Code Block |
---|
const IdVerificationPageSlot = () => (
<PluginSlot
id="id_verification_slot"
pluginProps={{
courseId,
}}
/>
<IdVerificationPage />
</PluginSlot>
)
...
subscribe(APP_READY, () => {
ReactDOM.render(
...
<Route path="/notifications/:courseId" element={<NotificationPreferences />} />
<Route path="/notifications" element={<NotificationCourses />} />
<Route path="/id-verification/*" element={<IdVerificationPageSlot />} />
<Route path="/" element={<AccountSettingsPage />} />
<Route path="/notfound" element={<NotFoundPage />} />
...
);
}); |
Refactoring
2U uses IDV on edx.org for the Name Affirmation feature, which verifies certain name changes requested by certificate-bearing learners using IDV. This is because a name change has the potential to change the name displayed on their certificate(s). On the backend, this is implemented by the edx-name-affirmation plugin, and, on the frontend, this is implemented by the NameChange component in the frontend-app-account MFE.
The VerifiedName model has a verification_attempt_id field, which currently stores the id
field of the corresponding SoftwareSecurePhotoVerification instance used to verify a particular name. This field poses a problem for the introduction of an additional IDV implementation because the value of this field on any given row could also refer to the id
field of a Persona IDV record. It would not be possible to determine whether a given id
value refers to a SoftwareSecurePhotoVerification model instance or a Persona IDV record model instance.
We propose amending the IDVerificationAttempt model to add a uuid
field, which would be unique across all IDV-related model instances. 2U would also add a uuid
field to the Persona IDV record model. This uuid
would become the ID that the edX platform uses to uniquely refer to IDV records. Some views and APIs would need to be updated as a result of this change.
Other Approaches Considered
Backend
As alternative approaches, we considered Software Secure IDV deprecation, an edx-platform fork, and direct edx-platform modification
IDV Deprecation
The current implementation of IDV using Software Secure has an unclear purpose in the platform. As is, it is our understanding that only 2U uses this integration. Based on relatively old threads in the Open edX Discuss forums, a few operators do use the Software Secure implementation but with modifications to suit their use cases (e.g. bypassing Software Secure to provide the ability for site operators to manually review photos). 2U, as the sole known user of this integration as it is currently implemented, no longer needs this integration, which could indicate deprecation of this integration.
Although Software Secure could be deprecated, the deprecation is not necessary to enable integration of additional IDV implementations. In fact, we believe that many, if not all, of the same extensibility mechanisms described above would be required. Additionally, because the Name Affirmation feature relies on Software Secure IDV, removal of Software Secure from the platform would also require us to consider how to remove the Name Affirmation feature. We fear that this will massively expand the scope of this project.
Besides Software Secure, we believe that the concept of “IDV” as a whole should be maintained, at least for now. There are other forms of IDV (i.e. manual, SSO, etc.) that the platform does use. Also, the concept of a “verified certificate” still exists in the platform and is used by the community. We believe that the deprecation of “IDV” as a concept would require far more work than we can afford and isn’t in direct service of our integration with Persona.
We believe that the proposal above will enable us to integrate with Persona without needing to perform any deprecation.
edx-platform Fork
We considered whether it was time for 2U to fork the edx-platform and make the necessary changes to the fork to integrate with Persona. We decided that this was an inappropriate direction. It is a very consequential decision, and the ability to integrate another IDV provider could be accomplished using extensibility mechanisms without resorting to a fork.
edx-platform Modification
We considered whether we could implement the Persona integration in the platform and provide a toggle to select between Software Secure and Persona as the platform IDV provider. However, this was largely a theoretical option. We understand that we must not commit 2U-specific code to the Open edX platform.
Frontend
As alternative approaches, we considered alternative PluginSlot locations, a server-side returned URL, a frontend-app-account MFE fork, and direct frontend-app-account MFE modification.
Alternative PluginSlot Locations
There are a few additional options for where to include a PluginSlot to allow integration with another IDV implementation, all of which relate to the use of the router that the frontend-app-account MFE uses for client-side routing.
Both options have significant issues compared to wrapping the IDVerificationPage
component in a PluginSlot, as described in the proposal above.
Adding Routes - PluginSlot as Sibling
The PluginSlot component could be a sibling of the current routes, which would allow us to add an additional route easily, but it would also mean hosting two IDV flows at different URLs.
It would also require us to be able to configure the route that the Name Affirmation feature uses when redirecting a learner to IDV, since the id-verification
route would already be reserved for the current Software Secure IDV flow.
Code Block |
---|
<Route path="/notifications/:courseId" element={<NotificationPreferences />} />
<Route path="/notifications" element={<NotificationCourses />} />
<Route path="/id-verification/*" element={<IdVerificationPage />} />
<Route path="/" element={<AccountSettingsPage />} />
<Route path="/notfound" element={<NotFoundPage />} />
<Route path="*" element={<NotFoundPage />} />
<PluginSlot
id="routes_slot"
pluginProps={{
courseId,
}}
/> |
Adding Routes - PluginSlot as Parent
The PluginSlot component could be a parent of the current routes. This option would allow us to have a single IDV flow and to reuse the existing id-verification
route. This would result in no necessary changes to the router or the route that the Name Affirmation feature uses. However, this option would make management of the existing non-IDV routes more challenging. It would require duplication and maintenance of the existing non-IDV routes on http://edx.org .
Code Block |
---|
<PluginSlot
id="routes_slot"
pluginProps={{
courseId,
}}
/>
<Routes>
<Route path="/notifications/:courseId" element={<NotificationPreferences />} />
<Route path="/notifications" element={<NotificationCourses />} />
<Route path="/id-verification/*" element={<IdVerificationPage />} />
<Route path="/" element={<AccountSettingsPage />} />
<Route path="/notfound" element={<NotFoundPage />} />
<Routes>
</PluginSlot> |
Server-Side Returned URL
In this option, a backend API on the LMS would return a Persona URL. The IDV URL is already exposed via the CoursewareMeta view, so a Django setting for the IDV URL could be introduced, which would be returned by the IDVerificationService.get_verify_location method.
However, the current use of the router that the frontend-app-account MFE uses for client-side routing does not allow routing to non-application pages. This would be need to refactored to allow this kind of navigation.
This option could work, but it results in changes to the platform that do not feel very extensible or reusable.
frontend-app-account MFE Fork
This approach is analogous to the edx-platform Fork option but for the frontend. The same judgments apply to this approach.
frontend-app-account MFE Modification
This approach is analogous to the edx-platform Modification option but for the frontend. The same judgments apply to this approach.
Competitive Research
This proposal is concerned with adding the extensibility mechanisms necessary to integrate another form of IDV into the platform, which is an Open edX platform problem, so we did not perform any competitive research.
Implementation Plan
2U will be responsible for implementation and delivery. This project is schedule to start as soon as possible. We estimate around 6-8 weeks for implementation.
Long-Term Ownership/Maintainership
The Cosmonauts team at 2U is the owning team of the IDV feature. The Cosmonauts team would continue to own and maintain the feature, complete with the aforementioned extensibility mechanisms.
Discovery
In a pluggable solution, what IDV features or integrations into the platform must be supported?
Are there any IDV features or integrations that can be ignored or not considered or that are planned to deprecation that we should be aware of?
How does the community use IDV? I asked in a Discuss thread but did not receive very much feedback.
What is the longterm role of IDV in the edx-platform?
Is it acceptable to build out extensibility mechanisms that are sufficient enough only to enable an integration with Persona and that may not support Software Secure or other IDV vendors?
For example, in the certificates generation logic, we may need to add a filter to “collect IDV attempts” so that we can add in our Persona records. 2U does not gate certificate generation on IDV, but some operators may choose to leverage that code path via the ENABLE_CERTIFICATES_IDV_REQUIREMENT Django setting. This means we may not need to add a filter to enable that certificates behavior.
Is there guidance for where it is acceptable to use a
PluginSlot
on the frontend? What requirements exist? Can it be inserted anywhere?