
Si vous avez déjà essayé de construire une intégration pour un Learning Management System (LMS) comme Canvas, Moodle ou Blackboard, vous connaissez la douleur. Plus précisément, la douleur du LTI 1.3 (Learning Tools Interoperability).
Cela implique une danse complexe de connexions OIDC, de trousseaux JWKS, de signatures RSA et de clés privées. C'est censé être sécurisé, mais pour un développeur qui veut juste « intégrer une web app dans Canvas », ça peut vite virer au cauchemar.
Voici l'architecture que j'ai retenue : Node.js pour la couche de sécurité, Next.js pour le frontend, séparés délibérément plutôt que construits comme un seul monolithe.
🔗 Dépôt GitHub : github.com/paurushrai/lti-1.3-node-nextjs-template
🧐 Qu'est-ce que LTI 1.3, et pourquoi s'en soucier ?
LTI (Learning Tools Interoperability) est la norme qui permet à des outils externes (comme votre app) de communiquer avec un LMS (comme Canvas).
Avant, LTI 1.1 était simple : on partageait une « clé secrète » et une « clé consommateur ». Mais c'était peu sécurisé. LTI 1.3 adopte le modèle moderne de sécurité de l'information :
- OIDC (OpenID Connect) pour le flux de connexion.
- JWT (JSON Web Tokens) pour l'échange de données.
- Chiffrement à clé publique/privée (RSA256) plutôt qu'un secret partagé.
Cela le rend incroyablement sécurisé, mais aussi plus difficile à implémenter à partir de zéro.
🏗 L'architecture
Pour résoudre ça, nous n'allons pas construire un monolithe. Nous allons séparer notre fournisseur de sécurité de notre UI applicative.
La stack technique
- Backend (le garde de sécurité) : Node.js + Express +
ltijs(la librairie standard pour LTI 1.3 en Node.js, par Cvmcosta). - Frontend (l'expérience) : Next.js 14+ (App Router) + React + Tailwind CSS.
- Base de données : PostgreSQL (stocke les clés cryptographiques et les enregistrements de plateformes).
- Infrastructure : Docker Compose.
Le problème : l'apocalypse des « cookies tiers »
Un défi majeur avec les outils LTI, c'est qu'ils tournent dans une <iframe> au sein du LMS. Les navigateurs modernes (Safari, Chrome en navigation privée) bloquent les cookies tiers par défaut.
Si votre web app repose sur un cookie de session (connect.sid) ? Elle échouera sous Safari.
La solution : le flux de token « LTIaaS »
Ce projet implémente un pattern de token « sans état » pour contourner la limitation des cookies :
- Lancement depuis le LMS : le LMS envoie une requête à notre backend Node.js.
- Poignée de main : le backend vérifie la signature. Si elle est valide, il génère un ltik (LTI key) de courte durée, à usage unique.
- Redirection : le backend redirige l'iframe vers le frontend Next.js, en passant le token dans l'URL :
https://myapp.com/app?ltik=eyJhb... - Hydratation : l'app Next.js récupère ce token depuis l'URL.
- Appels API : le frontend utilise ce token comme Bearer token dans l'en-tête
Authorizationpour tous les appels API suivants.
Aucun cookie requis !
🚀 Comment mettre ça en place (étape par étape)
Voici comment faire tourner tout ça sur votre machine locale en cinq minutes.
1. Prérequis
Vous avez besoin de Docker. Vous avez aussi besoin de ngrok (ou d'un service de tunnel similaire), car les outils LTI doivent être servis en HTTPS. Impossible d'utiliser simplement localhost.
2. Cloner le dépôt
git clone https://github.com/paurushrai/lti-1.3-node-nextjs-template.git
cd lti-1.3-node-nextjs-template3. Configurer l'environnement
Copiez le fichier d'environnement d'exemple :
cp lti-server/.env.example lti-server/.envGénérez une clé aléatoire sécurisée et collez-la dans LTI_KEY dans .env.
4. Démarrer le tunnel
Démarrez ngrok sur le port 3000 (là où vit notre serveur Node).
ngrok http 3000Copiez votre URL https (par exemple, https://my-tool.ngrok-free.app).
5. Lancer l'application
Ouvrez à nouveau lti-server/.env et définissez UI_URL sur votre URL ngrok.
Maintenant, démarrez tout avec Docker :
docker-compose up --buildVous avez maintenant :
- Serveur LTI :
http://localhost:4000 - Client Next.js :
http://localhost:3000 - Postgres : connecté automatiquement.
🔌 Se connecter au LMS (le lien « magique »)
Autrefois, il fallait copier-coller manuellement des clés publiques et des URL. Ce projet prend en charge le LTI Dynamic Registration.
- Copiez votre URL d'enregistrement :
https://<votre-domaine-ngrok>/lti/register - Allez sur votre LMS (par exemple, Canvas Admin → Developer Keys).
- Sélectionnez « Dynamic Registration ».
- Collez l'URL et cliquez sur Register.
Boum. 💥 L'outil et le LMS échangent automatiquement leurs clés publiques. L'outil est installé.
🧪 Tester le flux
- Créez une évaluation ou un module générique dans votre LMS.
- Sélectionnez « Outil externe » et choisissez votre nouvel outil.
- Lancez-le.
- Vous verrez l'app Next.js se charger dans l'iframe.
- Elle affichera : « Welcome, [Your Name] » et votre rôle (Instructeur/Étudiant).
L'application a réussi à :
- Authentifier le lancement.
- Décoder votre profil utilisateur depuis les données de lancement LTI.
- Le transmettre en toute sécurité au frontend.
Auriez-vous séparé la couche de sécurité de l'UI de la même façon ?
Séparer la logique de protocole (Node/ltijs) de la logique applicative (Next.js) est ce qui a rendu ça gérable : la couche de sécurité reste ennuyeuse et auditable, et l'UI reste une app Next.js normale qui n'a rien à savoir sur OIDC ou JWKS.
Le code est sur GitHub si vous voulez le forker et partir de quelque chose qui gère déjà la poignée de main : lti-1.3-node-nextjs-template. Curieux de savoir si quelqu'un est déjà tombé sur un cas où séparer les deux couches ainsi a plus gêné qu'aidé.


