Skip to content

Commit

Permalink
Introduce LtiConfigView
Browse files Browse the repository at this point in the history
So this is just an idea - feel free to shut this down if this doesn't
really fit into the vision of the django-lti library.

Basically, I had an idea to add a new `config.json` route which would
depend on the registration uuid. At least with Canvas LMS, this endpoint
can be used to autoconfigure the LTI application, making integration
much simpler at the admin level. This way the application author can
specify exactly where the LTI App intends to be placed within the LMS,
like the XML config schema we were using in LTI 1.1. This is much more
reliable than manually configuring these fields each time the LTI
application is installed.

I'm assuming this will require some discussion and refinements, so I'm
opening this up as a draft PR.

This is something the application author can create themselves, here
is an example of that:
* https://github.com/ccnmtl/mediathread/blob/master/lti_auth/views.py#L135

And I've generalized that view which is included here.

I think that anything django-lti can pre-configure itself like this
would be beneficial to application developers. Personally I've found
LTI 1.3 pretty complicated to work with as it is, so I'd like for
a library like this to simplify whatever it can.
  • Loading branch information
nikolas committed Feb 6, 2025
1 parent eb91594 commit a7bb7b2
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ To allow LTI platforms to retrieve a JWKS and initiate a launch, add paths for
```python
...

from lti_tool.views import jwks, OIDCLoginInitView
from lti_tool.views import jwks, OIDCLoginInitView, LtiConfigView

urlpatterns = [
path(".well-known/jwks.json", jwks, name="jwks"),
path("init/<uuid:registration_uuid>/", OIDCLoginInitView.as_view(), name="init"),
path("<uuid:registration_uuid>/config.json", LtiConfigView.as_view()),
]

```
Expand Down
55 changes: 55 additions & 0 deletions lti_tool/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from django.http import (
HttpRequest,
HttpResponse,
Expand All @@ -6,10 +7,12 @@
JsonResponse,
)
from django.http.response import HttpResponseRedirect
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from urllib.parse import urljoin

from pylti1p3.contrib.django import DjangoCacheDataStorage, DjangoOIDCLogin

Expand Down Expand Up @@ -140,3 +143,55 @@ def handle_data_privacy_launch(
if return_url is None:
return HttpResponseForbidden(error_msg)
return HttpResponseRedirect(return_url)


class LtiConfigView(View):
"""
JSON configuration endpoint for LTI 1.3.
In Canvas LMS, an LTI Developer Key can be created via Manual
Entry, or by URL. This view provides the JSON necessary for URL
configuration in Canvas.
https://canvas.instructure.com/doc/api/file.lti_dev_key_config.html
"""
@staticmethod
def get_config_json(domain: str, uuid: str) -> dict:
oidc_init_uri = urljoin(
"https://{}".format(domain),
reverse("init", kwargs={"registration_uuid": uuid}))

return {
"title": settings.LTI_TOOL_CONFIGURATION["title"],
"description": settings.LTI_TOOL_CONFIGURATION["description"],
"oidc_initiation_url": oidc_init_uri,
"scopes": [
"https://purl.imsglobal.org/spec/lti-ags/scope/lineitem",
"https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly"
],
"public_jwk_url": urljoin(
"https://{}".format(domain), reverse("jwks")),
}

def config_hook(self, config: dict) -> dict:
"""
Override this method at the application level to customize the
configuration object. At the very least, `target_link_uri`
must be added to the config dictionary.
You can configure placements for the LTI application within
the LMS tool via options in the returned dictionary.
See reference documentation:
https://canvas.instructure.com/doc/api/file.lti_dev_key_config.html
"""
return config

def get(self, request: HttpRequest, *args, **kwargs) -> JsonResponse:
domain = request.get_host()
registration_uuid = kwargs.get("registration_uuid")
config_obj = LtiConfigView.get_config_json(domain, registration_uuid)

config_obj = self.config_hook(config_obj)

return JsonResponse(config_obj)

0 comments on commit a7bb7b2

Please sign in to comment.