Install the core library only:
pip install lti1p3platformInstall with Django support:
pip install lti1p3platform[Django]Install with FastAPI support:
pip install lti1p3platform[fastapi]The platform should prepare the launch by gathering the necessary context information, including details about the user, the course, and any custom parameters that need to be included in the launch request.
from lti1p3platform.ltiplatform import LTI1P3PlatformConfAbstract
from lti1p3platform.registration import Registration
class LTIPlatformConf(LTI1P3PlatformConfAbstract):
def init_platform_config(self, platform_settings, platform_key_set):
"""
register platform configuration
"""
registration = Registration() \
.set_client_id(platform_settings.client_id) \
.set_deployment_id(platform_settings.deployment_id) \
.set_launch_url(platform_settings.launch_url) \
.set_deeplink_launch_url(platform_settings.deeplink_launch_url) \
.set_oidc_login_url(platform_settings.oidc_login_url) \
.set_tool_key_set_url(platform_settings.key_set_url) \
.set_platform_public_key(platform_key_set.public_key) \
.set_platform_private_key(platform_key_set.private_key) \
.set_tool_redirect_uris([
"https://tool.example.com/lti/launch",
"https://tool.example.com/lti/deeplink",
])
self._registration = registration
def get_registered_platform(*args, **kwargs):
...
return LTIPlatformConf(*args, **kwargs)
# public JWK endpoint
def get_jwks(request, *args, **kwargs):
platform = get_registered_platform(*args, **kwargs)
return HttpResponseJSON(platform.get_jwks())Important – redirect URI allowlist:
set_tool_redirect_uris()is required. It defines the allowlist of redirect URIs that the tool is permitted to supply during an OIDC/LTI launch. Theredirect_urisent by the tool in the preflight response must exactly match one of the registered values, otherwise the launch will fail with aninvalid_request_urierror and render a local error page (not a redirect). All URIs must use HTTPS; plain HTTP is only accepted forlocalhost/127.0.0.1/::1during development.
The tool consumer (i.e., the LMS) sends a request to the tool provider's application to initiate the OIDC authentication flow.
from lti1p3platform.oidc_login import OIDCLoginAbstract
class OIDCLogin(OIDCLoginAbstract):
def set_lti_message_hint(self, **kwargs):
""" set your own lti_message_hint """
pass
def get_lti_message_hint(self):
""" get your lti_message_hint """
pass
def get_redirect(self, url):
"""
This will be invoked in initiate_login, and it depends on which web framework you are using.
Here is an example for Django framework:
"""
return HttpResponseRedirect(url)
def render_error_page(self, message, status_code):
"""
This will be invoked when the library decides the error must not be
returned as an OAuth redirect.
"""
return HttpResponse(message, status=status_code)
# Initiate login endpoint
def preflight_lti_1p3_launch(request, user_id, *args, **kwargs):
platform = get_registered_platform(*args, **kwargs)
oidc_login = OIDCLogin(request, platform)
# Redirect the current login user to the tool provider,
return oidc_login.initiate_login(user_id)The library decides whether an OIDC/login error should be returned as a redirect to the tool or rendered locally as an error page.
| Scenario | Error code | Behavior |
|---|---|---|
Unknown client_id |
unauthorized_client |
Redirect to redirect_uri with OAuth error params |
| Missing required params | invalid_request |
Redirect to redirect_uri with OAuth error params |
Wrong response_type |
unsupported_response_type |
Redirect to redirect_uri with OAuth error params |
Missing openid scope |
invalid_scope |
Redirect to redirect_uri with OAuth error params |
Bad login_hint |
invalid_request |
Redirect to redirect_uri with OAuth error params |
Expired lti_message_hint |
invalid_request |
Redirect to redirect_uri with OAuth error params |
| User not authorized | access_denied |
Redirect to redirect_uri with OAuth error params |
| User not logged in | login_required |
Redirect to redirect_uri with OAuth error params |
Invalid redirect_uri |
invalid_request_uri |
Render local error page |
| Internal signing/config error | server_error or temporarily_unavailable |
Render local error page |
For redirectable errors, the library appends error, error_description, and
state when available. For non-redirectable errors, render_error_page() is used.
The tool provider redirect to the platform's OIDC auth request endpoint. The platform received the auth request and it will do some little bit of validation, it needs to ensure user is login, also check the login_hint is matched with the user_id. The platform also could get the context from the lti_message_hint which is sent in the initiating request and do some other validation.
After all verifications, the platform will generate a id_token. The platform encodes all important launch message payload as a JWT and send it as id_token parameter in a post request to the tool launch url.
from lti1p3platform.message_launch import MessageLaunchAbstract
class LTI1p3MessageLaunch(MessageLaunchAbstract):
def render_launch_form(self, launch_data, **kwargs):
"""
This will be invoked in the last step of `lti_launch`.
So you could render a template in this method. This template should render a form, and then submit it to the tool's launch URL. There is a django example in framework/django/message_launch.py
"""
pass
def get_redirect(self, url):
"""
This will be invoked when launch validation fails with a redirectable
OAuth/OIDC error.
"""
return HttpResponseRedirect(url)
def render_error_page(self, message, status_code):
"""
This will be invoked when launch validation fails with a local-only
error such as `invalid_request_uri` or `server_error`.
"""
return HttpResponse(message, status=status_code)
def prepare_launch(self, preflight_response, **kwargs):
"""
You could do some other checks and get some contexts from `lti_message_hint` you've set in previous request
Also you could call these methods to prepare your own jwt payload:
- set_user_data
- set_resource_link_claim
- set_launch_context_claim
- set_custom_parameters_claim
Make sure do these things before lti_launch, it could send necessary launch parameters to the tool.
"""
pass
def lti_resource_link_launch(request, *args, **kwargs):
platform = get_registered_platform(*args, **kwargs)
message_launch = LTI1p3MessageLaunch(request, platform)
return message_launch.lti_launch(*args, **kwargs)lti_launch() uses the same policy as OIDC login:
- Redirect to the supplied
redirect_urifor redirectable OAuth/OIDC errors such asinvalid_request,unauthorized_client,unsupported_response_type,invalid_scope,access_denied, andlogin_required. - Render a local error page for non-redirectable or server-side failures such as
invalid_request_uri,server_error, andtemporarily_unavailable.
Prerequisite: tox and python 3.7, 3.8, 3.9, 3.10
If you are using pyenv virtualenv, you might need to install all python versions and run pyenv local 3.7.x 3.8.x 3.9.x 3.10.x at the first time.
cd lti1p3platform
tox