Chapitre 10.5 — Sécurité & conformité
⏱️ TL;DR — Appeler un service LTI Advantage inverse l’asymétrie du lancement : au lancement, la plateforme signe l’
id_tokenet ton outil vérifie (via le JWKS de la plateforme) ; pour appeler un service, ton outil signe leclient_assertionavec sa clé privée et la plateforme vérifie (via ton JWKS). Deux directions, mêmes primitives — revois 9.3. Sécuriser, c’est : demander les scopes au minimum (moindre privilège), protéger et faire tourner ta clé privée (rotation via JWKS multi-clés), valider proprement chaque échange. Et pour vendre : viser la certification 1EdTech LTI Advantage — le label que les académies et grands comptes exigent. Ce chapitre clôt la partie par une checklist de mise en production.
🎯 Objectifs
- Comprendre l’asymétrie inversée des clés entre lancement et appel de service.
- Appliquer le moindre privilège sur les scopes.
- Protéger et faire tourner la clé privée de l’outil (rotation par JWKS).
- Savoir pourquoi et comment viser la certification 1EdTech.
- Dérouler une checklist avant de mettre un outil LTI Advantage en production.
L’asymétrie, dans les deux sens
En LTI 1.3, tout repose sur la signature asymétrique (clé privée pour signer, clé publique — exposée en JWKS — pour vérifier). Ce que la Partie 9 a posé pour le lancement, LTI Advantage le retourne pour l’appel de service. C’est le point de sécurité à intégrer :
| Échange | Qui signe (clé privée) | Qui vérifie (JWKS de l’autre) | Le jeton |
|---|---|---|---|
| Lancement (Partie 9) | La plateforme | Ton outil (via le JWKS de la plateforme) | id_token |
| Appel de service (AGS/NRPS) | Ton outil | La plateforme (via ton JWKS) | client_assertion |
Autrement dit : au lancement, tu es le vérificateur ; pour appeler un service, tu deviens le signataire. C’est pour cela que, dès l’enregistrement (chapitre 9.5), chacun a donné son jwks_uri à l’autre : la plateforme t’a donné le sien (tu valides ses id_token), tu lui as donné le tien (elle valide tes client_assertion).
⚠️ Piège — Croire qu’une seule paire de clés suffit et confondre les rôles. Si tu ne publies pas de
jwks_uricôté outil, tu peux lancer (tu vérifies) mais jamais appeler un service (la plateforme ne peut pas vérifier ta signature) : AGS et NRPS échouent avec uninvalid_clientau token endpoint. Deux JWKS, deux directions.
📚 La spec — Le mécanisme est celui du LTI 1.3 Security Framework (1EdTech) : signature JWS (typiquement
RS256), clés publiées en JWKS, authentification client par JWT Bearer (client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer) au grantclient_credentials. Les primitives (JWT, JWS, JWKS,kid) sont celles vues en 9.3.
Moindre privilège : les scopes au strict nécessaire
Un access_token ne peut faire que ce que ses scopes autorisent. Demande exactement ce dont chaque partie de ton outil a besoin — pas plus :
| Ton outil fait… | Scope(s) à demander |
|---|---|
| Poster des notes uniquement | .../lti-ags/scope/score |
| Poster et gérer les colonnes | .../lti-ags/scope/score + .../lti-ags/scope/lineitem |
| Relire les notes (suivi) | .../lti-ags/scope/result.readonly |
| Lire le roster | .../lti-nrps/scope/contextmembership.readonly |
💡 Réflexe — Segmente tes jetons par usage : un jeton
scorepour le worker qui poste les notes, un jetoncontextmembership.readonlypour le service qui lit le roster. Si le worker de notes est compromis, il ne peut pas aspirer les rosters. Un jeton sur-scopé « pour simplifier » est une dette de sécurité — et certaines plateformes refusent (invalid_scope) un scope non prévu à ta config.
Protéger et faire tourner la clé privée
Ta clé privée d’outil signe tous tes appels de service : c’est un secret critique.
- Stockage : jamais dans le dépôt Git, jamais en clair dans une variable d’env commitée. Utilise un gestionnaire de secrets (coffre-fort, KMS). La clé publique correspondante, elle, est publiée dans ton
jwks_uri. kid: chaque clé porte unkid(key id). Tu le mets dans le header de tesclient_assertionpour que la plateforme choisisse la bonne clé publique dans ton JWKS.- Rotation sans coupure : c’est là que le JWKS brille. Pour changer de clé :
{
"keys": [
{ "kid": "studio-2026", "kty": "RSA", "use": "sig", "n": "...", "e": "AQAB" },
{ "kid": "studio-2027", "kty": "RSA", "use": "sig", "n": "...", "e": "AQAB" }
]
}- Publie la nouvelle clé (
studio-2027) dans ton JWKS à côté de l’ancienne. 2. Commence à signer avecstudio-2027(nouveaukiddans le header). 3. La plateforme, en lisant ton JWKS, trouve la clé correspondant aukid— aucune coupure. 4. Après une période de recouvrement (le temps que les caches JWKS expirent partout), retire l’ancienne clé.
⚠️ Piège — Retirer l’ancienne clé du JWKS trop tôt (avant l’expiration des caches côté plateformes). Les plateformes cachent ton JWKS : si tu supprimes
studio-2026alors qu’une plateforme a encore l’ancien cache et reçoit une assertion signée par la nouvelle qu’elle ne connaît pas — ou l’inverse — la vérification échoue. Chevauche toujours les clés le temps d’un cycle de cache.
💡 Réflexe — Traite la rotation comme une procédure planifiée, pas une urgence. Garde deux clés valides en régime normal (ancienne + nouvelle) pour pouvoir basculer instantanément en cas de compromission, sans attendre la propagation d’un nouveau JWKS.
La certification 1EdTech
1EdTech ne fait pas que publier les specs : il certifie les produits. Pour LTI Advantage, la certification atteste que ton outil respecte réellement Core + les services que tu implémentes (Deep Linking, AGS, NRPS), testés contre une suite de conformité de référence.
Pourquoi la viser :
- C’est un critère d’achat. Académies, universités et grands comptes exigent (ou fortement préfèrent) des outils certifiés : le label est un gage vérifiable, il évite à l’acheteur de tester lui-même ton implémentation.
- Ça te sort du « ça devrait marcher ». Passer la suite force à corriger les détails que le fil rouge a montrés :
scoreMaximumtoujours présent,gradingProgresscorrect, pagination NRPS suivie, réponse Deep Linking signée. La conformité se prouve. - C’est un référencement. Les produits certifiés figurent dans l’annuaire public de 1EdTech, consulté par les acheteurs.
🔌 Côté intégration — Sans certification, chaque établissement doit te croire sur parole et tester ton intégration lui-même : friction, doute, cycles de vente longs. Avec le label LTI Advantage, tu coches une case de l’appel d’offres d’entrée de jeu. Pour un éditeur EdTech, la certification n’est pas un luxe technique : c’est un actif commercial. Vise-la service par service (tu peux être certifié AGS sans l’être Deep Linking).
📚 La spec — La certification et l’annuaire des produits conformes sont gérés par 1EdTech (
1edtech.org). Chaque service (Deep Linking, AGS, NRPS) a sa suite de conformité ; la certification LTI Advantage suppose Core plus les services visés. Les modalités exactes évoluent — vois1edtech.org.
Checklist de mise en production
Avant d’ouvrir ton outil LTI Advantage à de vrais établissements :
Clés & signatures
- Clé privée d’outil hors dépôt, dans un gestionnaire de secrets.
-
jwks_uripublic et stable, exposant ta ou tes clés publiques aveckid. -
client_assertioncorrectement formé (iss=sub=client_id,aud=token endpoint,expcourt,jtiunique). - Procédure de rotation testée (chevauchement de clés, période de recouvrement).
Jetons & scopes
- Scopes demandés au minimum nécessaire, segmentés par usage.
-
access_tokenmis en cache jusqu’àexpires_in(marge incluse), par(iss, scope). - Gestion propre des erreurs token endpoint (
invalid_scope,invalid_client).
Services
- Endpoints de service (AGS
endpoint, NRPSnamesroleservice) persistés au lancement, pas seulement en mémoire de requête. - AGS :
scoreGiventoujours avecscoreMaximum;activityProgress/gradingProgresscorrects ;timestampfiable (horloge NTP). - AGS : trouver la colonne, sinon la créer — pas de doublons de
lineitem. - NRPS : pagination suivie (
Linkrel="next") ;name/emailtraités comme optionnels. - Deep Linking : réponse signée (JWT),
dataré-émis,accept_typesrespectés.
Conformité & données
- Minimisation : ne lire/stocker que le nécessaire ; e-mails de mineurs non conservés sans besoin (RGPD).
- Journalisation des échanges de service (diagnostic « la note n’est pas remontée »).
- Testé sur SCORM Cloud ou un LMS de test, puis via la suite de conformité 1EdTech.
- Objectif certification planifié, service par service.
🧭 Sur FormaCampus — Avant d’ouvrir le studio à cinq académies, l’équipe déroule la checklist : clé privée dans le coffre-fort,
jwks_uripublic aveckidstudio-2026, jetons segmentés (scorepour le worker de notes,contextmembership.readonlypour le roster), endpoints AGS/NRPS persistés, e-mails de mineurs non stockés. Puis elle passe les suites de conformité Deep Linking, AGS et NRPS de 1EdTech et décroche la certification LTI Advantage. Résultat concret : dans l’appel d’offres suivant, la case « outil certifié LTI Advantage » est cochée — le studio n’est plus « une appli sympa », c’est un produit d’intégration certifié. Le pari de la Partie 9 est tenu.
💡 Réflexe — Ne traite pas la sécurité et la conformité comme une dernière étape. Elles se construisent avec l’implémentation : chaque piège des chapitres précédents (
scoreMaximumoublié, pagination ignorée, réponse DL non signée, e-mails superflus stockés) est exactement ce que la suite de conformité et l’audit d’une académie vont chercher. Coder propre dès le départ, c’est se certifier sans douleur.
✏️ Exercices
Exercice 1 — Qui signe, qui vérifie ? Pour (a) un lancement et (b) un POST de note AGS, indique qui signe et avec quelle clé, et qui vérifie et avec quel JWKS.
✅ Solution
(a) Lancement : la plateforme signe l’id_token avec sa clé privée ; ton outil vérifie avec le JWKS de la plateforme. (b) POST de note AGS : ton outil signe le client_assertion avec sa clé privée ; la plateforme vérifie avec ton JWKS. C’est l’asymétrie inversée : tu passes de vérificateur (au lancement) à signataire (à l’appel de service).
Exercice 2 — Rotation. Tu dois remplacer ta clé de signature d’outil sans interrompre les renvois de notes des établissements en production. Décris les étapes.
✅ Solution
- Publier la nouvelle clé (nouveau
kid) dans tonjwks_uri, à côté de l’ancienne (JWKS multi-clés). 2. Signer les nouveauxclient_assertionavec la nouvelle clé (nouveaukiddans le header). 3. Laisser une période de recouvrement le temps que les caches JWKS des plateformes se rafraîchissent (les deux clés restent valides). 4. Retirer l’ancienne clé du JWKS seulement après cette période. Ne jamais supprimer l’ancienne clé avant expiration des caches (sinon échecs de vérification).
Exercice 3 — Scopes. Ton worker de notes demande, « pour simplifier », les scopes lineitem, score, result.readonly et contextmembership.readonly. Que corriges-tu et pourquoi ?
✅ Solution
Le worker de notes ne fait que poster des scores : il ne lui faut que .../lti-ags/scope/score (plus .../lti-ags/scope/lineitem s’il crée les colonnes). result.readonly et surtout contextmembership.readonly (lire le roster) n’ont rien à faire dans ce worker : moindre privilège. Le roster doit être lu par un autre composant, avec son jeton dédié. Ainsi, une compromission du worker de notes n’expose pas les données personnelles de la classe. Bonus : une plateforme peut refuser (invalid_scope) un scope non prévu à ta config.
🧠 Quiz de révision
1. En quoi l’appel de service inverse-t-il l’asymétrie du lancement ?
Au lancement, la plateforme signe l’id_token et l’outil vérifie (JWKS de la plateforme). Pour appeler un service, l’outil signe le client_assertion (sa clé privée) et la plateforme vérifie (JWKS de l’outil). Signataire et vérificateur échangent leurs rôles.
2. Pourquoi chacun a-t-il besoin du jwks_uri de l’autre ?
jwks_uri de l’autre ?Parce que les signatures vont dans les deux sens : l’outil vérifie les id_token de la plateforme (via le JWKS de la plateforme) et la plateforme vérifie les client_assertion de l’outil (via le JWKS de l’outil). Sans JWKS d’outil, aucun appel de service.
3. Comment fait-on tourner une clé sans coupure ?
En publiant la nouvelle clé dans le JWKS à côté de l’ancienne (multi-clés, kid distincts), en signant avec la nouvelle, puis en retirant l’ancienne seulement après l’expiration des caches JWKS des plateformes (période de recouvrement).
4. Quelle règle appliquer aux scopes ?
Le moindre privilège : demander exactement les scopes nécessaires, segmentés par usage (un jeton score pour les notes, un jeton contextmembership.readonly pour le roster). Jamais tout « pour simplifier ».
5. Pourquoi viser la certification 1EdTech LTI Advantage ?
Parce que c’est un critère d’achat des marchés éducatifs (académies, universités, grands comptes) : le label est un gage vérifiable de conformité, il figure dans l’annuaire public de 1EdTech, réduit la friction commerciale et force une implémentation propre (barème, gradingProgress, pagination, réponse DL signée).
Fin de la Partie 10. Ton outil se branche, choisit son contenu, renvoie ses notes, lit sa classe — et vise la certification. Place aux autres standards 1EdTech : Partie 11 — QTI, CC, OneRoster, Caliper — tests, paquets de cours, rostering et analytics.