Original LTI Provider Doc

Don’t be fooled by the date on this confluence page, this is a copy of a copy of a circa 2015 design doc about the LTI provider loaded here to preserve the history.

General Architecture

We plan to introduce a new django app in the LMS that encapsulates the LTI provider functionality. It will include:

  • Endpoints through which campus applications access LTI material.

  • Models to track LTI launch context.

  • Listeners that pass scores back to the LMS on edX grading events.

By building the LTI provider functionality into a separate app we will interact with existing XBlocks through their published interface, while encapsulating all LTI interactions in a single component. By keeping the LTI logic in one place rather than scattered through the system we will simplify both the LTI and the XBlock implementations. This design will also simplify design of search and audit mechanisms.

 

There are six major areas to be considered in this design:

  • LTI Launch: The invocation of edX content from a campus LMS.

  • Resource Presentation: How the edX content appears in the campus LMS, particularly with regard to navigation elements.

  • Resource Configuration: How a course creator sets up edX content to be accessible via LTI.

  • User Management: Integrating users introduced through LTI launch with the edX concept of a user.

  • Gradebook Integration: Passing grades back from a graded edX assignment to the campus LMS.

  • Analytics Integration: Producing useful analytic data for researchers.

This document will address each area in turn.

 

Implementation will proceed in stages, starting with a minimal viable product. We understand that this first version will not meet a great many use cases, but it will provide a foundation for subsequent development. The aim for Version 1.0 is to assume only the existing capabilities of the Open edX platform; later versions may rely on new features that are in the works or are planned for the future. As such, the design is concrete for Version 1.0, but includes more limited sketches for future versions.

Version 1

The first implementation of the feature will include several simplifications. We will plan to build beyond these restrictions fairly quickly, and so our implementation will not preclude easing them in a later release.

  • All users will be assumed to have (or be willing to create) an account on the Open edX instance where the courseware is hosted.

  • Access to courseware will be configured at the component level (i.e. a single leaf XBlock) for graded assignments (meaning that one LTI launch will map to one unit in a vertical). For ungraded assignments we will display either components or subsections.

  • Resource selection will be primitive, requiring the course designer to enable a unit as an LTI provider and then copy/paste some information to the tool consumer.

  • LTI provider functionality will be enabled at the course level (not on individual XBlocks).

 

Resource Configuration

There are two areas to address with regard to resource configuration:

  • Whether LTI users interact with an existing edX course or with a specialized course that is accessible only through LTI.

  • How a course designer marks edX content as available through LTI, and how a campus course admin discovers that material.

 

For this design we will assume that LTI access is enabled on a course that has been specifically designated for that purpose, and to which regular edX users have no access. This greatly simplifies the user management and permissioning model (described later), at the expense of forcing course admins to duplicate material. We can mitigate this somewhat by recommending that course designers clone an existing course by creating a new instance of an existing course using the course re-run feature. Future improvements to the Open edX OLX format that will simplify import and export of individual units between courses, or the proposed dynamic course instance feature may further minimize the overhead.

 

For Version 1.0, we will enable the LTI provider functionality at the course level. When a course has been flagged as an LTI provider, all of the units in that course will be made available as LTI tools. This is not ideal, since course designers may want to restrict what edX material can be made available through a campus course (maybe for copyright reasons). However, the risk of accidentally exposing material by publishing at too coarse a level is low. The LTI launch signature limits which campus LMS can access an LTI tool to those with a valid key and secret (eliminating the risk of a student accessing material through an unauthorized LTI consumer).

 

For the initial version, campus LMS admins will be required to copy and paste the LTI endpoint URL. Later iterations will enable resource discovery, where an campus course designer can browse the edX material available via LTI. Implementing this catalog will require coordination with current efforts towards a Learning Object Repository in edX, as well as a more sophisticated publishing and permissioning options.

Resource Presentation

For Version 1.0, graded edX content will be addressed at the leaf unit - all edX chrome and navigation will be disabled. This includes the header and footer, all course navigation elements (the left section navigation menu), and all section navigation (the section filmstrip at the top of the unit, and the previous/next buttons at the bottom). Ungraded content may be displayed at the subsection granularity, with the section navigation enabled.

Addressing at the leaf unit also means that we do not have to worry about edX features that modify the section tree, such as cohorted assignments or AB testing. Subsections that are exported via LTI may be modified by these features, and it will be the responsibility of the course designer to understand how they may affect the material displayed to LTI users.

The LTI Provider Django app will include a simple view that embeds a single XBlock, whether a leaf or a vertical. This way we will avoid cluttering the existing courseware views and templates with conditionals to control the chrome. We will take advantage of the recently-accepted API changes to enable access to a single XBlock, along with the associated JQuery plugin that displays the XBlock content.

By removing the navigation chrome from the page we will avoid the majority of ways in which a user can escape the minimal LTI interface and return to the full edX experience. However there are still ways in which this could happen, such as direct links in a unit’s content that direct the user to, say, the course’s forum pages. This is a difficult thing to avoid technically (and is likely impossible in the general case, where a URL could lead via a series of redirections back into edX). We should instead approach this issue through documentation for the course designer, potentially creating tools that try to detect links back to edX should this prove to be a major issue.

For future versions, this basic approach will still hold. To allow for complex XBlocks to be properly exported over LTI we must first address the following issues:

  • There must be some mechanism by which we can map a grading event at the leaf unit level to its parent in order to properly register grade changes at the composite XBlock.

  • We must either disable (or guarantee some predictable result) if a course uses any features that may modify the contents of a vertical.

LTI Launch

LTI Version

For this feature we will limit ourselves to implementing the LTI 1.1 specification. This provides the following:

  • Basic LTI launch information, including user and course identification.

  • The Basic Outcomes Service interface that we will use for grade passback.

 

At present we do not need to implement the newer LTI 2.0 specification. While LTI 2.0 is increasingly being implemented by the various campus LMS implementations it is still a somewhat new standard, and adopting it at this point would limit the systems that can connect to edX in the short term. As the design currently stands, there are no features in LTI 2.0 that are essential for implementation.

 

At some point in the medium term we may want to support the LTI 1.2 specification to allow for content_item messages. This would allow us to enable browsing for LTI enabled material, simplifying course creation on the campus LMS side. The LTI 1.2 specification is backward compatible with LTI 1.1, so enabling the content item extensions will not affect the current implementation plan.

 

Launch Workflow

An LTI tool consumer initiates an LTI session through a POST request to a well-known URL, configured on the Open edX side when enabling a unit as a tool provider. There are four possible outcomes of an LTI launch attempt:

Unauthenticated User

The POST request does not contain a cookie for the Open edX domain that hosts the material, meaning that the user is not authenticated to edX. Since in this initial version we require that all users have an Open edX account, we will redirect the user to the edX login page where they may create an account or log into an existing account. Once they have authenticated, they will be redirected back to the courseware page.

The LTI launch operation was initiated using a POST with a set of parameters that will be required once the user has returned from the login page. Since those parameters will no longer be available after the authentication redirect, we will store the LTI parameters in the database, and include the key to retrieve the parameters as part of the redirect URL.

Ungraded Launch

The POST request contains a cookie for the Open edX domain, indicating that the user is logged in. The LTI launch parameters are valid, but they do not contain all of the required parameters for the LTI Basic Outcomes Service (see below). In this case the launch can proceed, but the system will not pass a grade back to the LTI Tool Consumer. This is probably not an error, since there will be use cases where a campus course will not require a grade from an edX unit.

As in the previous case, we will store the LTI launch parameters in the database. This way we will simplify later code, since we will not have to differentiate between the authenticated or unauthenticated cases.

Graded Launch

The POST request contains a cookie, and the LTI parameters include those required for the outcomes service. In this case we will have to persist the outcomes service information in order to return a grade when the unit is graded. The parameters must be stored permanently since although a grade may be returned immediately on completion, it could also be passed at some arbitrary later time in the case of peer graded assignments, or potentially much later if the assignment is regraded.

If some, but not all, of the Basic Outcome Service parameters are provided (see below for the specific parameters), we will log an error and fall back to the ungraded launch case. This error will alert the edX admin that an LTI tool consumer sent a malformed launch request; the administrator can then follow up out of band with the tool consumer’s admin.

As above, we will store the LTI launch parameters, including the Basic Outcome Service parameters. When necessary, we can differentiate between the graded and ungraded launch cases by checking this record for the presence of the Outcome Service information.

Failed Launch

There are several reasons why an LTI launch may fail:

  • The LTI message has not been signed with a valid authorization key. This may indicate a MITM attack, and so the request will be denied (403 Forbidden status).

  • The XBlock being addressed does not exist or has not been enabled for use as an LTI provider (404 Not Found status).

  • The LTI launch parameters are invalid or incomplete, as defined in the LTI Launch Parameters section below (400 Bad Request status).

  • The launch has been initiated from a context that does not have authorization to view the unit, as defined in the Permissions section below (403 Forbidden status).

  • The user indicated by the LTI launch parameters does not have an appropriate role the view the unit (403 Forbidden status).

Launch Parameters

The LTI specification lists the valid parameters to an LTI launch. The vast majority of parameters are specified as optional; the only required parameters are lti_message_type, lti_version, and resource_link_id. These parameters together are insufficient for our needs; even the resource_link_id parameter cannot be relied upon to uniquely identify a launch instance, since it is not guaranteed to be unique between tool consumers.

For an ungraded launch, we require the following parameters:

  • roles: we need to determine whether the user has the appropriate role in the course to view the unit (see Permissions, below)

  • context_id: we need to know which course the unit was launched from to determine whether users in that course are authorized to access the unit (see Permissions).

We also require the following OAuth parameters to validate the message signature:

  • oauth_version

  • oauth_consumer_key

  • oauth_signature

  • oauth_signature_method

  • oauth_timestamp

  • oauth_nonce

For a graded launch, we will require the following additional parameters:

  • lis_result_sourcedid: a unique identifier that is used to identify a user/launch combination to pass back a grade.

  • lis_outcome_service_url: the URL of the outcomes service on the tool consumer.

The lis_result_sourcedid field is defined as a row in a campus system’s gradebook. this means that there may be valid cases where the lis_outcome_service_url is provided, but the lis_result_sourcedid is not (for example, if a staff member launches a graded assignment). This should not necessarily be considered an error, and in these cases we can treat the launch as ungraded. We will log an error only If the LTI launch identifies the user as a student and supplies the lis_outcome_service_url field but not the lis_result_sourcedid.  

We will use the following parameters if they are available. If not, we will use defaults:

  • launch_presentation_return_url: the URL to return the user to after the unit is complete. If absent, we will just show a blank page.

  • tool_consumer_instance_guid: a unique identifier for the tool consumer. If absent, we will rely on the oauth_consumer_key to identify the tool consumer.

We will defer to the edX installation’s localization settings, rather than using the information sent in the launch_presentation_locale field.

For Version 1.0 we will not require the user_id or any other personally identifiable information (since we require that the user have an edX account). In later versions we may require more information (see the User Management section below). In later versions we may also use the launch_presentation_document_target parameter to determine whether an LTI launch will be presented in an iframe or a new window, and may modify the page design if appropriate.

Consumer Secret and Key Management

The LTI protocol assigns a key/secret pair to all LTI consumers. The LTI launch request includes the application key, and contains an OAUTH signature that is calculated using the secret. That way the LTI producer can guarantee that the request was made by an authorized client.

 

We will assign a unique key/secret pair to all authorized LTI consumers. This is good practice in order to manage access at a fine-grained level (allowing us to expire key/secrets as required), and also provides a reliable mechanism to track the originating system for every LTI request. The LTI specification includes a tool_consumer_instance_guid field that uniquely identifies the installation. However this field is optional, so we cannot rely on it being present at all LTI launches. As such, using the key/secret pair gives us a fallback mechanism to track which consumer launches a request (and later perform access control based on that).

 

Generating the key/secret values is a rare operation (required once per LTI consumer for a given edX installation), and will require a system administrator role. For the MVP we can simply create the values using the Django Admin interface.

 

We will support the HMAC-SHA1 signing protocol for Version 1.0, potentially adding support for other protocols as needed. We will require that all LTI launches be properly signed, and will contain both a timestamp and nonce to prevent replay attacks. The nonces will be persisted in the database. We will expire nonces after a reasonable period (set as one day by default) in order to save tracking overhead, and require that requests have a timestamp within that window.

Outcomes Service

We will use the LTI Basic Outcomes Service (from LTI version 1.1) to pass grades back to the campus system. In cases where the Outcomes service is not available, we will not return grades to the LMS, but we will allow edX content to be launched over LTI.

There are two sources of grading notifications in the system today:

  • A synchronous grading event generated when a unit is completed.

  • Grading notifications generated through the Submissions API.

The new LTI Django app will include a service that listens for any grading event. When an event is detected on an assignment that was initiated via a graded LTI launch  (determined through the information that we stored on launch) we will report the grade back to the tool consumer. The listener service will be implemented in such a way that it is simple to add a new source of grading events should one ever be added to the edX platform.

The LTI Basic Outcome Service is implemented by a persistent endpoint on the LTI consumer. It provides three (optional, but recommended) endpoints for grading:

  • replaceResult

  • readResult

  • deleteResult

Of these three, we will only need to interact with replaceResult. Since there is no distinction made in the LTI spec between the initial setting of a grade and its subsequent update, we can use the same code for either case. If the service does not provide the replaceResult endpoint, we will log an error with the tool consumer admin if possible.

Grading notifications do not need to be sent synchronously (although they should happen in a timely manner). The notification itself is sent using a POST action to a URL specified during the LTI launch. We will implement the interactions with the Basic Outcome Service as an asynchronous task using Celery. For Version 1.0 we will assume that the service is reliable, but an important follow-up piece of work will be to handle its failure:

  • The Basic Outcome Service (or indeed the entire LTI consumer) may be temporarily unavailable. In this case we will follow a reasonable retry strategy before logging an error, with retries initiated by edX. We will rely on the Open edX administrator to follow up with the tool consumer administrator out of band, and provide a management command that allows the edX administrator to retry commands once the problem has been resolved.

  • The service may return unexpected results due to misconfiguration, version skew or similar issues. As in the previous case, we will log the error and expect the failure to be handled out of band.

  • The LTI specification states that tool providers should be able to handle the case where the URL of a consumer’s outcome service changes. This should be rare, but it is possible if the consumer is reconfigured (moved to a different domain, for example, or if the internals of the consumer change).

We can mitigate the third failure case by maintaining a map from tool consumer unique identifier to a list of all Basic Outcome Service endpoints that have been supplied through its LTI launches. This way, should the outcome service URL change, we will be able to retry other known endpoints for a given consumer.

User Management

For Version 1.0 we plan to keep the user management as simple as possible. To this end, we will require that all LTI users be logged into an existing account on the Open edX platform (meaning that they must create an account before accessing material through LTI). Later versions may relax this requirement.

 

By ensuring that all users have an edX account, we guarantee that the user properties that edX expects will be present (including the user name, display name and email address). Ensuring this means that we do not need to locate all places in the code that assumes these fields in order to make sure that they work without them, and lets us bypass the need to merge accounts.

 

It is our intention to allow for pseudo-anonymous users in a later version by dynamically creating an account for a user on their first LTI launch. The LTI protocol specifies a user_id parameter that must not change for all launches by a given user on a single LTI consumer. We can use this identifier to map the LTI launch to a generated edX user account in order to maintain per-user state.

 

For this to happen, we need to consider several issues:

  • The presence of a special type of user in the system (one that is not guaranteed to have all the attributes of a regular user), and the semantics to follow when a required property is missing.

  • The algorithm by which we generate unique usernames for dynamically created user accounts.

  • The conditions under which we can link or merge multiple user accounts (either dynamically generated or regular edX accounts).

  • The policies surrounding how to communicate with users should we need to. One possible solution would be to set up an e-mail proxying system that maps a user’s e-mail address (registered with the campus LMS) with the opaque user ID provided by the LTI launch.

  • Since a pseudo-anonymous user has not completed the registration form, we may need some mechanism to ensure that they are not launching content from an embargoed country. We may be able to manage this by restricting the key/secret pairs that we distribute.

 

The first step will be laborious, but reasonably straightforward. We will have to determine what the absolute minimum information a user account requires (bearing in mind that some LTI consumer use cases rule out providing personally identifiable information), and then audit the code to find places where potentially missing parameters will lead to unexpected results.

 

The question of merging accounts is a harder one to deal with, since it may raise questions around identity and merging history between users. A first step could be to allow accounts to simply reference one another, or to allow accounts to merge only when no history has been created. Should this use case be necessary in a future version, a complete design process will be necessary.

 

Permissions

For Version 1.0, we will make the following simplifications:

  • The LTI provider functionality will be enabled at the course level.

  • Course designers will be advised that all material accessible through LTI should be in a dedicated course that is not published to regular edX users.

  • Users will not be explicitly enrolled in the course, rather all course material will be accessible through LTI.

 

By ensuring that LTI-accessible material exists in its own course ensures that all interactions that a student has with the course will be launched via LTI. This way a user cannot use their regular edX account to enroll in a course and complete its graded assignments without launching them through LTI. Were that to occur, we would not be able to pass the grade back to the campus LMS, since we would have no record of the Basic Outcome Service parameters.

 

We will make all leaf units in an LTI-enabled course available to any user that launches it through LTI. This way we do not have to build a permission management system for Version 1.0. For the expected early use cases (where campus and edX courses generally map 1:1) this will be acceptable. For later versions, particularly when we start to build a discovery mechanism for LTI-enabled material, we will develop a richer and more fine-grained permissioning system that will allow content creators to define what material can be used in which campus courses. Our implementation of Version 1.0 will assume that such a model will be added later, and so will avoid hardcoding any assumptions around the current simplified model.

 

Analytics Integration

There are two concerns that we need to address for research analytics:

  • Researchers should have access to the enrollment information for LTI students.

  • Researchers should be able to differentiate users who entered the system via LTI from those who entered through the normal edX interface.

 

Since we will not explicitly enroll students in a course that is accessible over LTI, we must construct the enrollment separately. We will create a new analytic data file for LTI courses that mirrors the existing per-course enrollment data, populated with the edX and LTI user IDs of each student who has launched a given course at least once. We must track both the edX and LTI user IDs, since any single edX user may access the edX instance from more than one campus system. This file will also include information on the LTI consumer from which the course was accessed, allowing researchers to break down enrollment by campus system. 

 

In addition to enrollment data, we will tag any relevant log events with the LTI ID of the user who launched the content. This way researchers can filter regular edX uses by the absence of an LTI user ID, and can easily determine which accesses are associated with LTI launches without having to join an enrollment record with the course ID for each access.

 

Feature Management

We will use two levels of settings to enable the LTI provider feature:

  • A flag at the Open edX installation level to enable the feature globally.

  • A flag at the course level that enables LTI provider functionality for that individual course.

At some point in the future, we may add additional flags that enable functionality at the individual unit or section level.