Chapitre 10.2 — Deep Linking
⏱️ TL;DR — Deep Linking répond à une question simple : quel contenu de ton outil l’enseignant veut-il placer dans son cours ? Au lieu de coller une URL en dur, la plateforme lance ton outil dans un mode spécial — un
id_tokendont le claim.../lti/claim/message_typevautLtiDeepLinkingRequest— accompagné d’un claimdeep_linking_settings(qui contient notammentdeep_link_return_urletaccept_types). Ton outil affiche son catalogue, l’enseignant choisit, et ton outil renvoie unLtiDeepLinkingResponse: un JWT signé dont le claim.../lti-dl/claim/content_itemsporte le tableau des items choisis (ltiResourceLink,link,file,html,image). Ce JWT est POSTé audeep_link_return_url; la plateforme crée alors la ou les activités dans le cours. Pas de jeton d’accès ici : tout passe par le lancement et une réponse signée.
🎯 Objectifs
- Reconnaître un lancement
LtiDeepLinkingRequestet liredeep_linking_settings. - Construire un
LtiDeepLinkingResponse(JWT signé) avec descontent_items. - Distinguer les types d’items (
ltiResourceLink,link,file,html,image). - Comprendre où POSTer la réponse (
deep_link_return_url) et ce que la plateforme en fait.
Le scénario : l’enseignant configure l’activité
En Partie 9, l’enseignant branchait ton outil, puis une activité pointait vers une URL de lancement fixe. Problème : ton catalogue a 200 exercices. Comment l’enseignant dit-il « je veux celui-là, sur les fractions, niveau 6e » ? À la main, en copiant une URL ? Fragile et pénible.
Deep Linking renverse la logique : c’est ton outil qui présente le choix. Quand l’enseignant clique sur « Ajouter du contenu depuis FormaCampus », la plateforme lance ton outil en mode sélection. Tu affiches ton catalogue, il choisit, tu renvoies le choix structuré. La plateforme crée l’activité — avec, déjà, le bon exercice et ses paramètres.
Temps 1 — Le lancement LtiDeepLinkingRequest
C’est un lancement LTI 1.3 normal (tout le flux OIDC + JWT de la Partie 9 s’applique : validation state, signature, iss, aud, exp, nonce), avec deux différences dans les claims :
{
"https://purl.imsglobal.org/spec/lti/claim/message_type": "LtiDeepLinkingRequest",
"https://purl.imsglobal.org/spec/lti/claim/deep_linking_settings": {
"deep_link_return_url": "https://moodle.ecole.fr/mod/lti/contentitem_return.php?course=9271&id=42",
"accept_types": ["ltiResourceLink", "link"],
"accept_presentation_document_targets": ["iframe", "window"],
"accept_multiple": true,
"auto_create": false,
"title": "Choisir un exercice FormaCampus",
"data": "opaque-token-a-renvoyer-tel-quel"
}
}message_typevautLtiDeepLinkingRequest(au lieu deLtiResourceLinkRequest) : c’est ce qui te dit « affiche un sélecteur, pas du contenu ».- Le claim
deep_linking_settingsporte les règles du jeu :deep_link_return_url: l’URL où tu devras POSTer ta réponse. C’est la donnée capitale.accept_types: les types d’items que la plateforme accepte (iciltiResourceLinketlink). N’en renvoie pas d’autres.accept_multiple: l’enseignant peut-il choisir plusieurs items d’un coup ?data: un jeton opaque que tu dois ré-émettre tel quel dans ta réponse (la plateforme s’en sert pour retrouver son contexte — comme unstate).
💡 Réflexe — Lis
accept_typesavant d’afficher ton catalogue et filtre ce que tu proposes. Si la plateforme n’accepte queltiResourceLink, n’affiche pas de bouton « insérer une image ». Renvoyer un type non listé = réponse rejetée. Et mémorisedeep_link_return_urletdata: tu en auras besoin au temps 3.
Temps 2 — L’enseignant choisit
Rien de normé ici : c’est ton interface. Tu affiches ton catalogue (recherche, filtres par niveau/chapitre), l’enseignant sélectionne un ou plusieurs exercices. Tu prépares alors, pour chaque choix, un content item.
Temps 3 — La réponse LtiDeepLinkingResponse
Ta réponse est un JWT que TON outil signe (avec sa clé privée — comme le client_assertion du chapitre 10.1). Ici, l’émetteur, c’est toi : iss = ton client_id, aud = l’iss de la plateforme. Le payload :
{
"iss": "formacampus-studio-client-id",
"aud": "https://moodle.ecole.fr",
"iat": 1893455940,
"exp": 1893456240,
"nonce": "nc-9f2a",
"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/claim/deployment_id": "dep-1",
"https://purl.imsglobal.org/spec/lti-dl/claim/data": "opaque-token-a-renvoyer-tel-quel",
"https://purl.imsglobal.org/spec/lti-dl/claim/content_items": [
{
"type": "ltiResourceLink",
"title": "Entrainement calcul mental - fractions",
"text": "20 questions, correction immediate",
"url": "https://studio.formacampus.fr/lti/launch",
"custom": {
"chapitre": "fractions",
"niveau": "6e",
"exerciceId": "ex-4815"
},
"lineItem": {
"scoreMaximum": 20,
"label": "Calcul mental - fractions",
"resourceId": "ex-4815"
}
}
]
}Décryptage :
message_type=LtiDeepLinkingResponse: la plateforme sait que c’est un retour de sélection.- Le claim
.../lti-dl/claim/datare-émet exactement ledatareçu au temps 1. - Le claim
.../lti-dl/claim/content_itemsest le cœur : un tableau d’items choisis.
Ce JWT, tu le POST au deep_link_return_url, généralement via un formulaire auto-soumis dans le navigateur (comme le lancement, mais dans l’autre sens) :
POST /mod/lti/contentitem_return.php?course=9271&id=42 HTTP/1.1
Host: moodle.ecole.fr
Content-Type: application/x-www-form-urlencoded
JWT=eyJhbGciOiJSUzI1NiIsImtpZCI6InN0dWRpby0yMDI2In0.eyJpc3M...La plateforme vérifie ta signature (via ton jwks_uri), lit les content_items et crée l’activité dans le cours. Fin du parcours : l’exercice choisi est posé dans le cours, avec ses custom et, ici, sa colonne de note déjà prête.
Les types de content_items
Le champ type de chaque item dit à la plateforme quoi créer. Les cinq types :
type | Ce que ça crée | Champs clés |
|---|---|---|
ltiResourceLink | Une activité lançable de ton outil (relance LTI ultérieure). | url, title, custom, lineItem |
link | Un lien externe simple (pas un relancement LTI). | url, title, text |
file | Un fichier à télécharger. | url, title, mediaType |
html | Un fragment HTML inséré directement. | html |
image | Une image affichée. | url, width, height |
Le type roi est ltiResourceLink : c’est celui qui crée une activité relançable (à chaque clic, un vrai lancement LTI Core vers ton url), et qui peut embarquer une colonne de note via son sous-objet lineItem.
💡 Réflexe — Utilise le sous-objet
lineItemd’unltiResourceLinkpour créer la colonne de carnet en même temps que l’activité. La plateforme crée l’exercice et sa colonne (avecscoreMaximumetlabel) d’un coup. Tu n’auras plus qu’à y poster les scores via AGS (chapitre 10.3) : le Deep Linking a préparé le terrain de l’AGS.
⚠️ Piège — Renvoyer la réponse en clair (un simple JSON POST) ou avec un JWT non signé. La réponse Deep Linking doit être un JWT signé par ta clé privée : c’est ce qui prouve à la plateforme que la sélection vient bien de ton outil (et pas d’un tiers qui forgerait des
content_itemspointant ailleurs). Sans signature valide, la plateforme rejette — et parfois silencieusement (l’enseignant revient sur une page vide). Oublier de ré-émettredataest l’autre grand classique : la plateforme ne retrouve pas son contexte et abandonne.
🔌 Côté intégration — Deep Linking, c’est l’ergonomie qui fait gagner l’appel d’offres. Un concurrent qui oblige l’enseignant à copier des URLs perd face à un outil où l’on clique « Ajouter depuis X », on choisit dans un beau catalogue, et l’activité plus sa colonne de note apparaissent d’un coup. Les évaluateurs de LMS testent exactement ce parcours. C’est aussi un critère de la certification Deep Linking.
🧭 Sur FormaCampus — L’enseignant de 6e A clique « Ajouter un exercice FormaCampus » dans son Moodle. Le studio reçoit un
LtiDeepLinkingRequest, litaccept_types(ltiResourceLinkaccepté) et affiche son catalogue filtré sur « Mathématiques 6e ». L’enseignant choisit « Calcul mental — fractions ». Le studio renvoie unLtiDeepLinkingResponsesigné, avec unltiResourceLinkportantcustom.exerciceId = ex-4815et unlineItem(scoreMaximum: 20). Moodle crée l’activité et la colonne « Calcul mental — fractions » dans le carnet. Au chapitre suivant, le studio remplira cette colonne.
📚 La spec — Deep Linking est spécifié par LTI Deep Linking (1EdTech). Le lancement porte
.../lti/claim/message_type=LtiDeepLinkingRequestet.../lti/claim/deep_linking_settings; la réponse est un JWTLtiDeepLinkingResponseportant.../lti-dl/claim/content_items(et.../lti-dl/claim/data). Les types d’items et leurs champs exacts (dont le sous-objetlineItem) sont normés — vois1edtech.org.
✏️ Exercices
Exercice 1 — Lis les règles. Tu reçois un lancement dont deep_linking_settings contient "accept_types": ["ltiResourceLink"] et "accept_multiple": false. Que peux-tu (et ne peux-tu pas) renvoyer ?
✅ Solution
Tu peux renvoyer un seul item, de type ltiResourceLink. Tu ne dois pas : renvoyer plusieurs items (accept_multiple est false), ni renvoyer un link, file, html ou image (non listés dans accept_types). Ton interface doit donc limiter la sélection à un exercice et ne proposer que des activités relançables. Et ta réponse doit ré-émettre le data reçu, dans un JWT signé.
Exercice 2 — Complète l’item. Tu veux que l’exercice choisi crée aussi sa colonne de note (max 20) dans le carnet, et qu’au relancement ton outil sache qu’il s’agit de l’exercice ex-4815 sur les fractions. Écris le content_item.
✅ Solution
{
"type": "ltiResourceLink",
"title": "Calcul mental - fractions",
"url": "https://studio.formacampus.fr/lti/launch",
"custom": {
"exerciceId": "ex-4815",
"chapitre": "fractions"
},
"lineItem": {
"scoreMaximum": 20,
"label": "Calcul mental - fractions",
"resourceId": "ex-4815"
}
}Le sous-objet lineItem demande la création de la colonne (avec scoreMaximum et label) ; le custom transporte exerciceId et chapitre, que tu reliras dans le claim .../lti/claim/custom au relancement (LtiResourceLinkRequest) pour ouvrir directement le bon exercice.
🧠 Quiz de révision
1. Comment reconnaît-on un lancement Deep Linking ?
Au claim .../lti/claim/message_type qui vaut LtiDeepLinkingRequest (au lieu de LtiResourceLinkRequest), accompagné du claim .../lti/claim/deep_linking_settings.
2. Que contient deep_linking_settings d’essentiel ?
deep_linking_settings d’essentiel ?Notamment deep_link_return_url (où POSTer ta réponse), accept_types (les types d’items acceptés), accept_multiple, et data (jeton opaque à ré-émettre tel quel).
3. Qu’est-ce qu’un LtiDeepLinkingResponse, et qui le signe ?
LtiDeepLinkingResponse, et qui le signe ?C’est la réponse de ton outil : un JWT signé par la clé privée de l’OUTIL, avec message_type = LtiDeepLinkingResponse et le claim .../lti-dl/claim/content_items. Il est POSTé au deep_link_return_url.
4. Quels sont les types de content_items ?
content_items ?ltiResourceLink (activité relançable), link (lien simple), file (fichier), html (fragment HTML), image (image). Le plus courant est ltiResourceLink.
5. Comment Deep Linking prépare-t-il l’AGS ?
Un ltiResourceLink peut porter un sous-objet lineItem : la plateforme crée alors la colonne de note en même temps que l’activité. L’AGS n’aura plus qu’à poster les scores dans cette colonne.
Chapitre suivant : AGS : renvoyer des notes — remplir le carnet du LMS, le service qui crée la vraie valeur.