-
Notifications
You must be signed in to change notification settings - Fork 39
Dev: Configuration for LTI 1.3 Development
This document will soon be replaced when MyLA's new feature for LTI v1.3 configuration by URL is released. To preview the new document, see: Dev: Configuration for LTI 1.3 Devemopment NEW.
Your patience while these updates are made is appreciated. Your feedback is welcome.
To complete the steps in the next section, MyLA must be configured according to the project's README.md
document. It needs to be installed on a server that is accessible by Canvas. Refer to the next section for installing a simple server for development purposes.
LTI 1.3 requires the client, an LMS like Canvas, to contact the tool server, which hosts MyLA. That means when MyLA runs on a computer in a development environment, it needs to be available online in a way that the LMS can contact it via HTTPS. There are a number of ways that can be accomplished, but the easiest is to install ngrok and run it. It will dynamically create a hostname for the application and give a URL for it that can be accessed anywhere online. Requests to that URL will be forwarded to the local computer through a network tunnel.
-
Follow the installation instructions on ngrok's download page.
- Alternate installation methods (may not be officially supported by ngrok.com)
-
Using Homebrew (macOS or Linux)
brew cask install ngrok
-
Using npm
npm install ngrok
-
- Alternate installation methods (may not be officially supported by ngrok.com)
-
Add
.ngrok.io
to theALLOWED_HOSTS
array of MyLA'senv.json
configuration file. For example:"ALLOWED_HOSTS": [ "127.0.0.1", "localhost", ".ngrok.io" ],
-
Start MyLA on the local computer using Docker:
docker-compose up
It runs on port 5001 by default.
-
Start a ngrok forwarding tunnel:
ngrok http 5001
💡 – ngrok's "http" tunnel automatically handles both HTTP and HTTPS.
While ngrok runs, it offers some helpful troubleshooting and debugging interfaces:
-
https://dashboard.ngrok.com/status/tunnels – Shows the active forwarding tunnels for the local computer. This requires free registration with ngrok.com. Additional features become available with a paid registration.
-
http://127.0.0.1:4040 – View the incoming requests and inspect them.
-
To programmatically get the URL of the first running tunnel:
# if jq is installed curl -s http://127.0.0.1:4040/api/tunnels | \ jq -r '.tunnels[0].public_url' # without jq curl -s http://127.0.0.1:4040/api/tunnels | \ sed 's/^.*"public_url":"\([^"]*\)".*$/\1/'
-
Once a server is hosting
To enable LTI launch requests, MyLA needs a public and private RS256 key pair in PEM format. For some LTI features, the LMS need to use a JWK format of the public key to communicate with MyLA, so that may be needed as well. For the most part, the JWK public key should not be used. It's safer to give a URL to MyLA's /lti/jwks/
service instead, which generates the JWK public key from the PEM public key on demand. It is still generated here mostly for the purposes of troubleshooting the configuration.
💡 – When configuring MyLA's LTI support it is important to note that the two formats of public keys are used for different purposes.
- The public key PEM is used for LTI launch requests.
- The public key JWK is used by the LMS for secure communication with an already running LTI.
There are currently two ways to generate those three key files.
🚧 – NEW: This section documents a new feature that will be available soon.
MyLA includes a key generation program, createkeys
, that can be run via the Django management tool, manage.py
. While MyLA is running in a Docker container, enter the following command to create key files:
docker exec -it student_dashboard python manage.py createkeys
The program will create the new key files in the same directory that holds the env.json
configuration file.
It will create three files with the suffixes _private.pem
, _public.pem
, and _public-jwk.json
. By default, the three files will be named with a timestamp of the format YYYYmmddHHMMSS
, followed by one of the suffixes. If you want to specify a different name for the key files, use the --basename
option:
docker exec -it student_dashboard python manage.py createkeys --basename example
That will create key files named example_private.pem
, example_public.pem
, and example_public-jwk.json
.
🚧 – DEPRECATED: This section is deprecated and will be removed as soon as the feature described in the "Django Management Tool" section above becomes available.
The Python program below generates, the three key files required:
# Prerequisites:
# pip install pycryptodome
# pip install jwcrypto
import json
from Crypto.PublicKey import RSA
from jwcrypto.jwk import JWK
private_key_output_filename = 'example_private.pem'
public_key_output_filename = 'example_public.pem'
jwk_output_filename = 'example_public-jwk.json'
print('Generating private and public keys...')
key = RSA.generate(4096)
print('Preparing private and public key strings...')
private_key = key.exportKey()
public_key = key.publickey().exportKey()
print(f'Writing private key to file "{private_key_output_filename}"...')
with open(private_key_output_filename, 'w') as f:
f.writelines((private_key.decode('utf-8'), '\n'))
print(f'Writing public key to file "{public_key_output_filename}"...')
with open(public_key_output_filename, 'w') as f:
f.writelines((public_key.decode('utf-8'), '\n'))
jwk_obj = JWK.from_pem(publicKey)
public_jwk = {
**json.loads(jwk_obj.export_public()),
**{'alg': 'RS256', 'use': 'sig'}
}
print(f'Writing JWK to file "{jwk_output_filename}"...')
with open(jwk_output_filename, 'w') as f:
f.writelines((json.dumps(public_jwk), '\n'))
That will create key files named example_private.pem
, example_public.pem
, and example_public-jwk.json
.
Move the three key files to the same directory as the env.json
configuration file for MyLA.
- The CSP block of the settings can be copied and should work "as-is". ngrok.io is part of those settings but would change for your production server.
- Change
"REPORT_ONLY"
to false if there are no console errors in your browser when testing - You'll also need
"CSRF_COOKIE_SECURE": true
when running on HTTPS.
⚠️ – The hostnameEXAMPLE_NGROK_ID.ngrok.io
appears several times in the following configuration examples. It must be replaced with the hostname of the computer on which MyLA is being tested.
-
There are a number of LTI settings including
-
"STUDENT_DASHBOARD_LTI" : true
This setting enables LTI support.
-
-
Create Developer keys (Go to Canvas "Key Settings" page, following these instructions)
Use ONE of the two following sections, "Manual Entry" or "Paste JSON"
-
Redirect URIs:
https://EXAMPLE_NGROK_ID.ngrok.io/lti/launch/
-
Target Link URI:
https://EXAMPLE_NGROK_ID.ngrok.io/lti/launch/
-
OpenID Connect Initiation Url:
https://EXAMPLE_NGROK_ID.ngrok.io/lti/login/
-
JWK Method: Public JWK URL
-
Public JWK URL:
https://EXAMPLE_NGROK_ID.ngrok.io/lti/jwks/
💡 – In the near future, the LTI specification will require JWK to be specified by URL only. To ensure compatibility, do not paste the contents of the JWK into a configuration property. Only use the
public_jwk_url
property to provide a URL to MyLA's JWK, likehttps://EXAMPLE_NGROK_ID.ngrok.io/lti/jwks/
.
-
-
LTI Advantage Services: Enable all toggles
-
Additional Settings
-
Domain:
EXAMPLE_NGROK_ID.ngrok.io
-
Custom Fields
user_username=$User.username canvas_user_id=$Canvas.user.id canvas_course_id=$Canvas.course.id
-
Privacy Level: Public
-
Placements: Choose "Course Navigation" only, remove any others added by default
- Course Navigation section
- Target Link URI:
https://EXAMPLE_NGROK_ID.ngrok.io/lti/launch/
- Target Link URI:
- Course Navigation section
-
Click the "Save" button
-
Find the new key and click the ✎ (pencil) icon to edit it.
-
Select the "Paste JSON" method if it's not selected already
-
In the "LTI 1.3 Configuration" field, find the "placements" array
-
In that array, add the following to the first object:
"default": "disabled",
That will prevent the tool from being installed in courses by default.
-
-
Copy the following JSON and paste into the "LTI 1.3 Configuration" field
- Change the
title
anddescription
attributes, if desired.
💡 – In the near future, the LTI specification will require JWK to be specified by URL only. To ensure compatibility, do not paste the contents of the JWK into a configuration property. Only use the
public_jwk_url
property to provide a URL to MyLA's JWK, likehttps://EXAMPLE_NGROK_ID.ngrok.io/lti/jwks/
.{ "title": "My Learning Analytics Test", "scopes": [ "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem", "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly", "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly", "https://purl.imsglobal.org/spec/lti-ags/scope/score", "https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly", "https://canvas.instructure.com/lti/public_jwk/scope/update", "https://canvas.instructure.com/lti/account_lookup/scope/show", "https://canvas.instructure.com/lti/data_services/scope/create", "https://canvas.instructure.com/lti/data_services/scope/show", "https://canvas.instructure.com/lti/data_services/scope/update", "https://canvas.instructure.com/lti/data_services/scope/list", "https://canvas.instructure.com/lti/data_services/scope/destroy", "https://canvas.instructure.com/lti/data_services/scope/list_event_types", "https://canvas.instructure.com/lti/feature_flags/scope/show" ], "extensions": [ { "domain": "EXAMPLE_NGROK_ID.ngrok.io", "platform": "canvas.instructure.com", "settings": { "platform": "canvas.instructure.com", "placements": [ { "default": "disabled", "placement": "course_navigation", "message_type": "LtiResourceLinkRequest", "target_link_uri": "https://EXAMPLE_NGROK_ID.ngrok.io/lti/launch/" } ] }, "privacy_level": "public" } ], "public_jwk": {}, "description": "This is LTI key for LTI1.3", "custom_fields": { "user_username": "$User.username", "canvas_user_id": "$Canvas.user.id", "canvas_course_id": "$Canvas.course.id" }, "public_jwk_url": "https://EXAMPLE_NGROK_ID.ngrok.io/lti/jwks/", "target_link_uri": "https://EXAMPLE_NGROK_ID.ngrok.io/lti/launch/", "oidc_initiation_url": "https://EXAMPLE_NGROK_ID.ngrok.io/lti/login/" }
- Change the
-
Change "State" from OFF to ON for the newly created key
-
Install the LTI APP to your course/subaccount/root account as you like, since with the default: disabled option the tool won't enabled in course navigation
-
Create new External App: "Settings -> Apps -> +App"
-
Choose "Configuration Type: by ClientID"
-
Insert "ClientID" from the created Key (value from Details column)
-
Update settings in
LTI_CONFIG
section ofenv.json
"https://canvas.instructure.com": [ { "/* requests without ID from this platform use this config by default": "*/", "default": true, "/* from Canvas: Developer Keys -> value from Details column": "*/", "client_id": "10000000000004", "/* static auth_login_url URL from this platform": "*/", "auth_login_url": "http://canvas.instructure.com/api/lti/authorize_redirect", "/* static auth_token_url URL from this platform": "*/", "auth_token_url": "http://canvas.instructure.com/login/oauth2/token", "/* static URL to get public key from this platform": "*/", "key_set_url": "http://canvas.instructure.com/api/lti/security/jwks", "/* this tool's private key ": "*/", "private_key_file": "/secrets/example_private.pem", "/* this tool's public key ": "*/", "public_key_file": "/secrets/example_public.pem", "/* Go to Canvas where this tool installed, click on the setting button (a gear icon, similar to '⚙️'), click 'Deployment Id', copy it, and paste here": "*/", "deployment_ids": [ "6:8865aa05b4b79b64a91a86042e43af5ea8ae79eb" ] } ]
-
Enable the LTI in the Course navigation, depending on where you have installed the LTI tool (course, root account, or subaccount)
- Go to the course where MyLA was installed
- Settings -> Navigation
- Enable the MyLA tool and save
- Tool will appear in left navigation
After the configuration changes are complete, the MyLA Docker pods will probably need to be restarted in order for them to take effect.
After launch the tool from course navigation if the course doesn't exits in MyLA DB it will be created only by instructor and MyLA Django admin's only and UI may display message something like "Course data is not pulled in yet" Make yourself as admin locally by going to django_auth
table and change the is_superuser
column value to 1. Run the cron the course data will be pulled in and when the LTI launched you should get see courses view page
Then if you "Act as a User" in that course in Canvas, click the link in the Navigation you should be able to get right in.
When developing locally with ngrok and webpack watch mode the initial launch takes time since the main-*.js
is not compressed in watch mode. This shouldn't be an issue when running in prod mode
There is a setting ENABLE_BACKEND_LOGIN
which is set to False
now if LTI or SAML are enabled. If you set this to True
the standard internal login/logout will work. This should probably only be enabled for testing.
MyLA uses a third party module that implements LTI 1.3 workflows and validation. See the pylti1.3 GitHub repo for additional documentation.