Skip to content
Snippets Groups Projects
user avatar
michaelroytman authored
  * Adds an Lti1p3ProctoringLaunchData data class. It should be included as an attribute of the Lti1p3LaunchData
    data class to provide necessary proctoring data for a proctoring launch.
  * Adds an LtiProctoringConsumer class. This class is used to generate LTI proctoring launch requests and to decode
    and validate the JWT send back by the Tool with the LtiStartAssessment message.
  * Adds an lti_1p3_proctoring_enabled BooleanField to the LtiConfiguration model. This field controls whether
    proctoring is enabled for a particular LTI integration.
  * Modifies the launch_gate_endpoint to support LtiStartProctoring and LtiEndAssessment LTI launch messages.
  * Adds an start_proctoring_assessment_endpoint to support LtiStartAssessment messages from the Tool.
  * Adds an LTI_1P3_PROCTORING_ASSESSMENT_STARTED signal. This signal is emitted when the LtiStartAssessment message is
    sent from the Tool to inform users of the library that the LtiStartAssessment message has been received.
6288b2a0
History

LTI 1.3 Consumer Class

This implements a LTI 1.3 compliant consumer class which is request agnostic and can be reused in different contexts (XBlock, Django plugin, and even on other frameworks).

This doesn't implement any data storage, just the methods required for handling LTI messages and Access Tokens.

Features:

  • LTI 1.3 Launch with full OIDC flow
  • Support for custom parameters claim
  • Support for launch presentation claim
  • Access token creation

This implementation was based on the following IMS Global Documents:

Using this class

To perform LTI launches, you'll need to store and manage a few resources and endpoints.

Data storage

LTI variables from tool:

  • lti_oidc_url: The tool's OIDC login initiation URL, needs to be stored and passed to the consumer every time it's instanced.
  • lti_launch_url: The tool's LTI launch URL, where the platform submit's the actual LTI launch request with the signed LTI message.
  • tool_key: The tool's public key, in raw PEM format. This will be used to verify message signatures from the tool to the platform. This is not required if this module is just used to launch LTI tools (without LTI advantage support).

LTI configuration from platform:

  • client_id: Tool specific client ID, to separate multiple tool configurations (can be the same as the rsa_key_id).
  • deployment_id: Deployment specific key ID (spec reference). Used if deploying multi-tenant instances of LTI consumer, otherwise just a fixed string known be the tool.
  • rsa_key: a raw PEM export of a RSA key. The minimum required is a SHA-256 (RS256) key (and also recommended to maximize interoperability).
  • rsa_key_id: the key id for the RSA key above. Should be unique for every key used for LTI platform wide to avoid signature validation issues.

Endpoints

To run LTI launches, 2 endpoints are required:

  • Launch Callback URL: URL in the platform that the Tool will redirect to with response variables from preflight request made to OIDC endpoints.
  • Keyset URL: URL that the tool will use to fetch the public key of the platform (a JWK as defined in RFC7517). This URL should be publicly accessible and return the contents of the get_public_keyset function as a JSON.

Example implementation

Here's a example LTI Launch using Django in the edX platform.

def _get_lti1p3_consumer():
	"""
    Returns an configured instance of LTI consumer.
    """
    return LtiConsumer1p3(
      # Tool urls
      lti_oidc_url=lti_1p3_oidc_url,
      lti_launch_url=lti_1p3_launch_url,
      # Platform and deployment configuration
      iss=get_lms_base(),
      client_id=lti_1p3_client_id,
      deployment_id="1",
      # Platform key
      rsa_key=lti_1p3_block_key,
      rsa_key_id=lti_1p3_client_id,
      # Tool key
      tool_key=lti_1p3_tool_public_key,
    )


def public_keyset(request):
    """
    Return LTI Public Keyset url.

    This endpoint must be configured in the tool.
    """
    return JsonResponse(
      _get_lti1p3_consumer().get_public_keyset(),
      content_type='application/json'
    )

def lti_preflight_request(request):
    """
    Endpoint that'll render the initial OIDC authorization request form
    and submit it to the tool.

    The platform needs to know the tool OIDC endpoint.
    """
    lti_consumer = _get_lti1p3_consumer()
    context = lti_consumer.prepare_preflight_url()

    # This template should render a simple redirection to the URL
    # provided by the context through the `oidc_url` key above.
    # This can also be a redirect.
    return render(request, 'templates/lti_1p3_oidc.html', context)

def lti_launch_endpoint(request):
    """
    Platform endpoint that'll receive OIDC login request variables and generate launch request.
    """
    lti_consumer = _get_lti1p3_consumer()
    context = {}

    # Required user claim data
    lti_consumer.set_user_data(
      user_id=request.user,
      # Pass django user role to library
      role='student'
    )

    context.update({
      "preflight_response": dict(request.GET),
      "launch_request": lti_consumer.generate_launch_request(
        resource_link=self.resource_link_id,
        preflight_response=request.GET
      )
    })

    context.update({'launch_url': self.lti_1p3_launch_url})
    # This template should render a form, and then submit it to the tool's launch URL, as
    # described in http://www.imsglobal.org/spec/lti/v1p3/#lti-message-general-details
    return render(request, 'templates/lti_launch_request_form.html', context)