diff --git a/lti/deep_linking.py b/lti/deep_linking.py index a82e5265..c1e2d415 100644 --- a/lti/deep_linking.py +++ b/lti/deep_linking.py @@ -213,14 +213,21 @@ class SelectMediaView(View): 'https://purl.imsglobal.org/spec/lti/claim/version': '1.3.0', 'https://purl.imsglobal.org/spec/lti/claim/deployment_id': deployment_id, 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items': lti_content_items, - 'https://purl.imsglobal.org/spec/lti-dl/claim/data': deep_linking_settings.get('data', ''), } + # Echo back data claim if it was present in the request + if 'data' in deep_linking_settings: + payload['https://purl.imsglobal.org/spec/lti-dl/claim/data'] = deep_linking_settings['data'] + print("JWT Payload:") print(f" iss (issuer): {tool_issuer}") print(f" aud (audience): {audience}") - print(f" deployment_id: {deployment_id}") + print(f" deployment_id: {deployment_id} (type: {type(deployment_id).__name__})") print(f" content_items count: {len(lti_content_items)}") + print(" Full payload:") + import json + + print(json.dumps(payload, indent=2, default=str)) # Sign JWT with tool's private key kid = key_obj.private_key_jwk['kid'] diff --git a/lti/urls.py b/lti/urls.py index 05837c1e..99c1e94d 100644 --- a/lti/urls.py +++ b/lti/urls.py @@ -13,6 +13,7 @@ urlpatterns = [ path('oidc/login/', views.OIDCLoginView.as_view(), name='oidc_login'), path('launch/', views.LaunchView.as_view(), name='launch'), path('jwks/', views.JWKSView.as_view(), name='jwks'), + path('public-key/', views.PublicKeyPEMView.as_view(), name='public_key_pem'), # Deep Linking path('select-media/', deep_linking.SelectMediaView.as_view(), name='select_media'), # LTI-authenticated pages diff --git a/lti/views.py b/lti/views.py index 689d00b5..3014197d 100644 --- a/lti/views.py +++ b/lti/views.py @@ -15,7 +15,7 @@ import uuid from urllib.parse import urlencode import jwt -from django.http import HttpResponseRedirect, JsonResponse +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.utils.decorators import method_decorator @@ -362,6 +362,40 @@ class JWKSView(View): return JsonResponse(jwks, content_type='application/json') +class PublicKeyPEMView(View): + """ + Display public key in PEM format for easy copy/paste into Moodle + """ + + def get(self, request): + """Return public key in PEM format""" + from jwcrypto import jwk + + from .models import LTIToolKeys + + # Get key from database + key_obj = LTIToolKeys.get_or_create_keys() + + # Convert to PEM + jwk_obj = jwk.JWK(**key_obj.public_key_jwk) + pem_bytes = jwk_obj.export_to_pem() + pem_string = pem_bytes.decode('utf-8') + + # Return as plain text for easy copy/paste + return HttpResponse( + f"MediaCMS LTI Public Key (PEM Format)\n" + f"{'=' * 80}\n\n" + f"{pem_string}\n" + f"{'=' * 80}\n\n" + f"Instructions:\n" + f"1. Copy the entire key above (including BEGIN/END lines)\n" + f"2. In Moodle LTI tool configuration, change 'Public key type' to 'Public key'\n" + f"3. Paste the key into the 'Public key' field\n" + f"4. Save and try Deep Linking again\n", + content_type='text/plain', + ) + + @method_decorator(xframe_options_exempt, name='dispatch') class MyMediaLTIView(View): """