
Se hai mai provato a costruire un'integrazione per un Learning Management System (LMS) come Canvas, Moodle o Blackboard, conosci il dolore. Nello specifico, il dolore di LTI 1.3 (Learning Tools Interoperability).
Comporta una danza complessa di login OIDC, keyset JWKS, firme RSA e chiavi private. È pensato per essere sicuro, ma per uno sviluppatore che vuole semplicemente "incorporare una web app in Canvas", può sembrare un incubo.
Questa è l'architettura a cui sono arrivato: Node.js per il livello di sicurezza, Next.js per il frontend, separati apposta invece di essere costruiti come un monolite.
🔗 Repository GitHub: github.com/paurushrai/lti-1.3-node-nextjs-template
🧐 Cos'è LTI 1.3 e perché dovrebbe interessarti?
LTI (Learning Tools Interoperability) è lo standard che permette a strumenti esterni (come la tua app) di comunicare con un LMS (come Canvas).
In passato, LTI 1.1 era facile: condividevi una "chiave segreta" e una "chiave consumer". Ma era insicuro. LTI 1.3 passa al modello moderno di sicurezza informatica:
- OIDC (OpenID Connect) per il flusso di login.
- JWT (JSON Web Token) per lo scambio di dati.
- Crittografia a chiave pubblica/privata (RSA256) invece di segreti condivisi.
Questo lo rende incredibilmente sicuro, ma anche più difficile da implementare da zero.
🏗 L'architettura
Per risolvere questo, non costruiremo un monolite. Separeremo il nostro fornitore di sicurezza dalla nostra UI applicativa.
Lo stack tecnologico
- Backend (la guardia di sicurezza): Node.js + Express +
ltijs(la libreria Node.js standard per LTI 1.3, di Cvmcosta). - Frontend (l'esperienza): Next.js 14+ (App Router) + React + Tailwind CSS.
- Database: PostgreSQL (memorizza le chiavi crittografiche e le registrazioni delle piattaforme).
- Infrastruttura: Docker Compose.
Il problema: l'apocalisse dei "cookie di terze parti"
Una sfida importante con gli strumenti LTI è che girano dentro un <iframe> all'interno dell'LMS. I browser moderni (Safari, Chrome in incognito) bloccano i cookie di terze parti per impostazione predefinita.
Se la tua web app si affida a un cookie di sessione (connect.sid)? Fallirà su Safari.
La soluzione: il flusso di token "LTIaaS"
Questo progetto implementa un pattern di token "stateless" per aggirare il limite dei cookie:
- Lancio dall'LMS: l'LMS invia una richiesta al nostro backend Node.js.
- Handshake: il backend verifica la firma. Se valida, genera un ltik (LTI key) monouso e di breve durata.
- Redirect: il backend reindirizza l'iframe verso il frontend Next.js, passando il token nell'URL:
https://myapp.com/app?ltik=eyJhb... - Idratazione: l'app Next.js recupera questo token dall'URL.
- Chiamate API: il frontend usa questo token come Bearer token nell'header
Authorizationper tutte le chiamate API successive.
Nessun cookie richiesto!
🚀 Come impostare tutto (passo dopo passo)
Ecco come farlo girare sulla tua macchina locale in cinque minuti.
1. Prerequisiti
Ti serve Docker installato. Ti serve anche ngrok (o un servizio di tunneling simile) perché gli strumenti LTI devono essere serviti via HTTPS. Non puoi semplicemente usare localhost.
2. Clona il repository
git clone https://github.com/paurushrai/lti-1.3-node-nextjs-template.git
cd lti-1.3-node-nextjs-template3. Configura l'ambiente
Copia il file di ambiente di esempio:
cp lti-server/.env.example lti-server/.envGenera una chiave sicura casuale e incollala in LTI_KEY dentro .env.
4. Avvia il tunnel
Avvia ngrok sulla porta 3000 (dove vive il nostro server Node).
ngrok http 3000Copia il tuo URL https (ad es. https://my-tool.ngrok-free.app).
5. Avviare l'app
Riapri lti-server/.env e imposta UI_URL sul tuo URL ngrok.
Ora, avvia tutto con Docker:
docker-compose up --buildOra hai:
- Server LTI:
http://localhost:4000 - Client Next.js:
http://localhost:3000 - Postgres: connesso automaticamente.
🔌 Connettersi all'LMS (il link "magico")
Ai vecchi tempi, dovevi copiare e incollare manualmente chiavi pubbliche e URL. Questo progetto supporta la Dynamic Registration LTI.
- Copia il tuo URL di registrazione:
https://<your-ngrok-domain>/lti/register - Vai nel tuo LMS (ad es. Canvas Admin → Developer Keys).
- Seleziona "Dynamic Registration".
- Incolla l'URL e clicca Register.
Boom. 💥 Lo strumento e l'LMS si scambiano automaticamente le chiavi pubbliche. Lo strumento è ora installato.
🧪 Testare il flusso
- Crea un compito o un elemento modulo generico nel tuo corso LMS.
- Seleziona "External Tool" e scegli il tuo nuovo strumento.
- Lancialo.
- Vedrai l'app Next.js caricarsi dentro l'iframe.
- Mostrerà: "Welcome, [Your Name]" e il tuo ruolo (Instructor/Student).
L'app ha eseguito con successo:
- L'autenticazione del lancio.
- La decodifica del tuo profilo utente dai dati di lancio LTI.
- Il passaggio sicuro al frontend.
Separeresti il livello di sicurezza dalla UI allo stesso modo?
Separare la logica del protocollo (Node/ltijs) dalla logica applicativa (Next.js) è stato ciò che ha reso il tutto gestibile: il livello di sicurezza resta noioso e verificabile, e la UI resta una normale app Next.js che non deve sapere nulla di OIDC o JWKS.
Il codice è su GitHub, se vuoi farne un fork e partire da qualcosa che gestisce già l'handshake: lti-1.3-node-nextjs-template. Sono curioso di sapere se a qualcuno è capitato un caso in cui separare i due livelli in questo modo ha creato più intralci che benefici.


