django-lti-provider provides Learning Tools Interoperability (LTI) functionality for the Django web framework. This work began as a port of MIT's LTI Flask Sample, which demonstrates a sample LTI provider for the Flask Framework based on the Python LTI library, PyLTI.
Additional work was completed to provide fuller functionality and support the idiosyncrasies of various LMS systems such as Canvas, Blackboard, Moodle and EdEx.
LTI 1.3 support is currently in progress, using the PyLTI1p3 library. See django-lti for LTI 1.3 support.
django-lti-provider offers:
- an authentication backend to complete an oAuth handshake (optional)
- a templated view for config.xml generation
- a templated landing page view for those LMS who do not have a 'launch in new tab' option, i.e. Canvas
- support for Canvas' embedded tool extensions
- routing for multiple external assignment end points.
The library is used at Columbia University's Center for Teaching And Learning.
See an example Django app using the library at Django LTI Provider Example.
You can install django-lti-provider
through pip
:
$ pip install django-lti-provider
Or, if you're using virtualenv, add django-lti-provider
to your requirements.txt
.
Add to INSTALLED_APPS
in your settings.py
::
'lti_provider',
Add the URL route:
path('lti/', include('lti_provider.urls'))
Add the LTIBackend to your AUTHENTICATION_BACKENDS:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'lti_provider.auth.LTIBackend',
]
Complete a migration
./manage.py migrate
The LTI_TOOL_CONFIGURATION
variable in your settings.py
allows you to
configure your application's config.xml and set other options for the library. (Edu Apps has good documentation
on configuring an lti provider through xml.)
LTI_TOOL_CONFIGURATION = {
'title': '<your lti provider title>',
'description': '<your description>',
'launch_url': 'lti/',
'embed_url': '<the view endpoint for an embed tool>' or '',
'embed_icon_url': '<the icon url to use for an embed tool>' or '',
'embed_tool_id': '<the embed tool id>' or '',
'landing_url': '<the view landing page>',
'course_aware': <True or False>,
'course_navigation': <True or False>,
'new_tab': <True or False>,
'frame_width': <width in pixels>,
'frame_height': <height in pixels>,
'custom_fields': <dictionary>,
'allow_ta_access': <True or False>,
'assignments': {
'<name>': '<landing_url>',
'<name>': '<landing_url>',
'<name>': '<landing_url>',
},
}
To stash custom properties in your session, populate the LTI_PROPERTY_LIST_EX
variable in your settings.py
. This is useful for LMS specific custom_x
parameters that will be needed later. The default value for LTI_PROPERTY_LIST_EX
is: ['custom_canvas_user_login_id', 'context_title', 'lis_course_offering_sourcedid', 'custom_canvas_api_domain']
.
LTI_PROPERTY_LIST_EX = ['custom_parameter1', 'custom_parameter2']
For simplest scenarios you can store data for the LTI request in a session cookie.
This is the quickest way to get up and running, and due to Django's tamper
proof cookie session (assuming a secure secret key) it is a safe option.
Please note that you will need to add the following settings in your
applications settings.py
to make use of cookies:
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
SESSION_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SECURE = True
Because Canvas sends the information that we are storing in a POST
request on the LTI launch, we need to relax the restriction of cookies
only being allowed to be set from the same site. For more information on
SESSION_COOKIE_SAMESITE
read here.
For more information on why SESSION_COOKIE_SAMESITE
and SESSION_COOKIE_SECURE
are needed, if you are choosing to make use of cookies, please read
here.
To specify a custom username property, add the LTI_PROPERTY_USER_USERNAME
variable to your settings.py
. By default, LTI_PROPERTY_USER_USERNAME
is custom_canvas_user_login_id
. This value can vary depending on your LMS.
To pass through extra LTI parameters to your provider, populate the LTI_EXTRA_PARAMETERS
variable in your settings.py
.
This is useful for custom parameters you may specify at installation time.
LTI_EXTRA_PARAMETERS = ['lti_version'] # example
The PYLTI_CONFIG
variable in your settings.py
configures the
application consumers and secrets.
PYLTI_CONFIG = {
'consumers': {
'<random number string>': {
'secret': '<random number string>'
}
}
}
Since LTI tools live within an iframe on Canvas, you might need
adjust your X_FRAME_OPTIONS
setting to allow for the LTI tool to be
opened within the iframe. To the best of our knowledge you probably
don't have to adjust this setting, as Canvas has built a workaround.
For more info read here
This ensures that the Django application will allow requests from your
orgs Canvas instance. For more on X_FRAME_OPTIONS
please
consult here.
If you happen to have a deployment scenario where you have load balancer
listening on https and routing traffic to nodes that are listening to HTTP,
you will need to add the following line of configuration in settings.py
:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
This ensures the correct launch_url
is generated for the LTI tool.
For more on this setting, read here.
To support multiple assignments:
- Create multiple endpoint views
- Add the assignment urls to the
LTI_TOOL_CONFIGURATION['assignments']
map - Add an assignment, using the External Tool option.
- Update the URL to be
https://<your domain name>/lti/assignment/<assignment_name>
- The
assignment_name
variable should match a landing_url in the LTI_TOOL_CONFIGURATION dict. - Full example here: Django LTI Provider Example.
OR
- Create a single named endpoint that accepts an id
- On Post, django-lti-provider will attempt to reverse the assignment_name/id and then redirect to that view.