15 février 2026 · 6 min read

LTI DeepLinking

Offrez la possibilité de choisir vos exercices

#LTI #e-learning #standard #LMS

DeepLinking (DL)

Comme nous l’avons vu dans le post LTI dans les détails, intégrer un outil LTI nécessite une étape de registration c’est-à-dire un échange d’informations entre l’outil et le LMS pour établir un lien de confiance. Une fois cette relation établie, un administrateur configure l’outil et les enseignants peuvent l’ajouter dans leurs cours autant de fois qu’ils le souhaitent. Le problème apparaît quand l’outil tiers propose un catalogue d’exercices. Chaque exercice nécessite sa propre configuration, donc son propre passage par la registration. Pour dix exercices, dix enregistrements. C’est un frein réel à l’adoption. C’est ce que Deep Linking résout. Au lieu de multiplier les configurations, l’enseignant lance l’outil directement depuis Moodle, navigue dans le catalogue proposé par l’outil, sélectionne l’exercice de son choix (ou plusieurs), et un lien direct vers cette ressource est automatiquement créé dans le cours.

Activer l’option deep linking dans le LMS

Lorsque un administrateur d’un LMS souhaite autorisé un outil à proposer un catalogue de ressources il doit le configurer au moment de la registration.

Par exemple avec Moodle :

Activation DeepLinking dans Moodle

Cela permet à l’enseignant de pouvoir choisir une ressource (exercice, video etc…). moodle présente un bouton Selectionner du contenu au moment d’intégrer l’outil dans un cours.

bouton selection de contenu dans Moodle

Le flux de lancement Deep Linking

Le lancement se déroule exactement de la même façon que le launch standard décrit dans le post LTI dans les détails avec pour seule différence le message_type du JWT qui vaut LtiDeepLinkingRequest au lieu de LtiResourceLinkRequest. C’est ce claim qui indique à l’outil qu’il ne doit pas afficher une ressource, mais proposer une interface de sélection.

Moodle initie la session exactement comme pour un launch classique, il crée un formulaire HTML auto-soumis en POST vers l’endpoint de l’outil. L’enseignant est redirigé vers l’outil avec un JWT signé contenant le contexte du cours, son identité, ses rôles, et un claim supplémentaire deep_linking_settings qui décrit ce que Moodle accepte comme types de retour.

L’outil prend alors le contrôle. Il vérifie le JWT comme il le ferait pour un launch standard, identifie l’enseignant et son contexte, puis affiche son interface de sélection — un catalogue d’exercices, un explorateur de ressources, un formulaire de création. La spec laisse l’outil entièrement libre sur cette expérience. Moodle peut ouvrir ce flux dans une iframe ou une popup pour que l’interface du cours reste visible en arrière-plan, mais ce n’est pas une obligation.

Une fois la sélection faite, l’outil construit un JWT de réponse — le LtiDeepLinkingResponse — contenant la description de la ressource choisie, et le POST vers l’URL deep_link_return_url fournie par Moodle dans le claim deep_linking_settings. Moodle reçoit ce JWT, vérifie sa signature, et crée l’activité dans le cours.

Un détail important : l’outil doit toujours rediriger vers deep_link_return_url, même si l’enseignant n’a rien sélectionné. C’est ce qui permet à Moodle de fermer proprement l’iframe ou la popup ouverte au départ.

Le LtiDeepLinkingRequest

Au moment du lauch l’id token devient :

{
  "nonce": "fd6383991c494f1db873aea6eac5f7184933707b33d911f1b1cd8cb87ec90090",
  "iat": 1775714375,
  "exp": 1775714435,
  "iss": "http://localhost",
  "aud": "v6NOc0mEFNce1wI",
  "https://purl.imsglobal.org/spec/lti/claim/deployment_id": "2",
  "https://purl.imsglobal.org/spec/lti/claim/target_link_uri": "http://localhost:8000/lti/launch/",
  "sub": "2",
  "https://purl.imsglobal.org/spec/lti/claim/lis": {
    "person_sourcedid": "",
    "course_section_sourcedid": ""
  },
  "https://purl.imsglobal.org/spec/lti/claim/roles": [
    "http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator",
    "http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor",
    "http://purl.imsglobal.org/vocab/lis/v2/system/person#Administrator"
  ],
  "https://purl.imsglobal.org/spec/lti/claim/context": {
    "id": "2",
    "label": "pyLTI",
    "title": "PYTHON LTI",
    "type": [
      "CourseSection"
    ]
  },
  "https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiDeepLinkingRequest",
  "https://purl.imsglobal.org/spec/lti/claim/launch_presentation": {
    "locale": "fr"
  },
  "https://purl.imsglobal.org/spec/lti/claim/ext": {
    "lms": "moodle-2"
  },
  "https://purl.imsglobal.org/spec/lti/claim/tool_platform": {
    "product_family_code": "moodle",
    "version": "2025100603.11",
    "guid": "aa85caf65877b6e8f2e5cfd314c9805e",
    "name": "local",
    "description": "localhost"
  },
  "https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0",
  "https://purl.imsglobal.org/spec/lti/claim/custom": {
    "niveau": "Terminale",
    "debut_cours": "2026-04-03T23:00:00+00:00",
    "fin_cours": "2027-04-03T23:00:00+00:00"
  },
  "https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings": {
    "accept_types": [
      "ltiResourceLink"
    ],
    "accept_presentation_document_targets": [
      "frame",
      "iframe",
      "window"
    ],
    "accept_copy_advice": false,
    "accept_multiple": true,
    "accept_unsigned": false,
    "auto_create": false,
    "can_confirm": false,
    "deep_link_return_url": "http://localhost/mod/lti/contentitem_return.php?course=2&id=2&sesskey=ifDNXFlCBh",
    "title": "Django - LTI DL",
    "text": ""
  }
}

On remarque deux choses :

"https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings": {
    "accept_types": [
      "ltiResourceLink"
    ],
    "accept_presentation_document_targets": [
      "frame",
      "iframe",
      "window"
    ],
    "accept_copy_advice": false,
    "accept_multiple": true,
    "accept_unsigned": false,
    "auto_create": false,
    "can_confirm": false,
    "deep_link_return_url": "http://localhost/mod/lti/contentitem_return.php?course=2&id=2&sesskey=ifDNXFlCBh",
    "title": "Django - LTI DL",
    "text": ""
  }

Le LMS affiche alors la page renvoyé par l’outils. Il s’agit d’une page HTML représentant un catalogue de ressource.

Moodle Select Content

Le LtiDeepLinkingResponse

Une fois l’enseignant a sélectionné sa ressource, l’outil construit un JWT signé avec sa clé privée et le poste vers la deep_link_return_url via un formulaire HTML auto-soumis.

<form
  id="lti13_deep_link_auto_submit"
  action="http://localhost/mod/lti/contentitem_return.php?course=2&id=2&sesskey=ifDNXFlCBh"
  method="POST"
>
  <input type="hidden" name="JWT" value="eyJ0eXAiOiJKV1Qi..." />
</form>
<script type="text/javascript">
  document.getElementById("lti13_deep_link_auto_submit").submit();
</script>

L’action du formulaire est exactement la deep_link_return_url reçue dans le claim deep_linking_settings. Le JWT est posté dans un champ caché nommé JWT. Le script soumet le formulaire sans aucune interaction de l’enseignant, il est redirigé automatiquement vers Moodle qui reçoit la réponse.

Une fois décodé, le JWT contient :

{
  "iss": "v6NOc0mEFNce1wI",
  "aud": [
    "http://localhost"
  ],
  "exp": 1775762232,
  "iat": 1775761632,
  "nonce": "nonce-349d991133b044a896847b62f53d1f4f50c92e71344711f198a08cb87ec90090",
  "https://purl.imsglobal.org/spec/lti/claim/deployment_id": "2",
  "https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiDeepLinkingResponse",
  "https://purl.imsglobal.org/spec/lti/claim/version": "1.3.0",
  "https://purl.imsglobal.org/spec/lti-dl/claim/content_items": [
    {
      "type": "ltiResourceLink",
      "title": "Variables et types",
      "url": "http://localhost:8000/lti/launch/",
      "custom": {
        "exercice": "variables"
      }
    }
  ],
  "https://purl.imsglobal.org/spec/lti-dl/claim/data": null
}

iss et aud sont inversés par rapport au request car c’est maintenant l’outil qui s’identifie comme émetteur et Moodle comme destinataire. Moodle vérifie la signature avec la clé publique de l’outil déclarée lors de la registration.

Partager