
Se você já tentou construir uma integração para um Sistema de Gestão de Aprendizagem (LMS) como Canvas, Moodle ou Blackboard, conhece a dor. Especificamente, a dor do LTI 1.3 (Learning Tools Interoperability).
Envolve uma dança complexa de logins OIDC, keysets JWKS, assinaturas RSA e chaves privadas. É pensado para ser seguro, mas para um desenvolvedor que só quer "embutir um web app no Canvas", pode parecer um pesadelo.
Esta é a arquitetura em que cheguei: Node.js para a camada de segurança, Next.js para o frontend, separados de propósito em vez de construídos como um monólito.
🔗 Repositório no GitHub: github.com/paurushrai/lti-1.3-node-nextjs-template
🧐 O Que É o LTI 1.3 e Por Que Você Deveria se Importar?
LTI (Learning Tools Interoperability) é o padrão que permite que ferramentas externas (como seu app) se comuniquem com um LMS (como o Canvas).
No passado, o LTI 1.1 era fácil: você compartilhava uma "chave secreta" e uma "chave de consumidor". Mas era inseguro. O LTI 1.3 migra para o modelo moderno de segurança da informação:
- OIDC (OpenID Connect) para o fluxo de login.
- JWTs (JSON Web Tokens) para troca de dados.
- Criptografia de chave pública/privada (RSA256) em vez de segredos compartilhados.
Isso o torna incrivelmente seguro, mas também mais difícil de implementar do zero.
🏗 A Arquitetura
Para resolver isso, não vamos construir um monólito. Vamos separar nosso provedor de segurança da nossa UI da aplicação.
O Stack Tecnológico
- Backend (o guarda de segurança): Node.js + Express +
ltijs(a biblioteca padrão de Node.js para LTI 1.3, feita por Cvmcosta). - Frontend (a experiência): Next.js 14+ (App Router) + React + Tailwind CSS.
- Banco de dados: PostgreSQL (guarda chaves criptográficas e registros de plataforma).
- Infraestrutura: Docker Compose.
O Problema: O Apocalipse do "Cookie de Terceiros"
Um grande desafio com ferramentas LTI é que elas rodam dentro de um <iframe> dentro do LMS. Navegadores modernos (Safari, Chrome Incógnito) bloqueiam cookies de terceiros por padrão.
Se o seu web app depende de um cookie de sessão (connect.sid)? Vai falhar no Safari.
A Solução: O Fluxo de Token "LTIaaS"
Este projeto implementa um padrão de token "stateless" para contornar a limitação de cookies:
- Lançamento do LMS: o LMS envia uma requisição para o nosso backend Node.js.
- Handshake: o backend verifica a assinatura. Se válida, gera um ltik (LTI key) de uso único e curta duração.
- Redirecionamento: o backend redireciona o iframe para o frontend Next.js, passando o token na URL:
https://myapp.com/app?ltik=eyJhb... - Hidratação: o app Next.js captura esse token da URL.
- Chamadas de API: o frontend usa esse token como Bearer token no header
Authorizationpara todas as chamadas de API futuras.
Nenhum cookie necessário!
🚀 Como Configurar Isso (Passo a Passo)
Aqui está como rodar isso na sua máquina local em cinco minutos.
1. Pré-requisitos
Você precisa do Docker instalado. Também precisa do ngrok (ou um serviço de tunelamento parecido) porque ferramentas LTI precisam ser servidas via HTTPS. Você não pode simplesmente usar localhost.
2. Clone o Repositório
git clone https://github.com/paurushrai/lti-1.3-node-nextjs-template.git
cd lti-1.3-node-nextjs-template3. Configure o Ambiente
Copie o arquivo de ambiente de exemplo:
cp lti-server/.env.example lti-server/.envGere uma chave segura aleatória e cole no LTI_KEY dentro do .env.
4. Inicie o Túnel
Inicie o ngrok na porta 3000 (é onde nosso servidor Node vive).
ngrok http 3000Copie sua URL https (ex.: https://my-tool.ngrok-free.app).
5. Rodando o App
Abra o lti-server/.env de novo e defina UI_URL como sua URL do ngrok.
Agora, suba tudo com Docker:
docker-compose up --buildAgora você tem:
- Servidor LTI:
http://localhost:4000 - Cliente Next.js:
http://localhost:3000 - Postgres: conectado automaticamente.
🔌 Conectando ao LMS (O Link "Mágico")
Antigamente, você tinha que copiar e colar chaves públicas e URLs manualmente. Este projeto suporta Registro Dinâmico do LTI.
- Copie sua URL de registro:
https://<seu-dominio-ngrok>/lti/register - Vá até seu LMS (ex.: Canvas Admin → Developer Keys).
- Selecione "Dynamic Registration".
- Cole a URL e clique em Register.
Pronto. 💥 A ferramenta e o LMS trocam as chaves públicas automaticamente. A ferramenta agora está instalada.
🧪 Testando o Fluxo
- Crie um módulo ou tarefa genérica no seu curso do LMS.
- Selecione "External Tool" e escolha sua nova ferramenta.
- Lance-a.
- Você verá o app Next.js carregar dentro do iframe.
- Ele vai exibir: "Welcome, [Seu Nome]" e seu papel (Instrutor/Aluno).
O app conseguiu com sucesso:
- Autenticar o lançamento.
- Decodificar seu perfil de usuário a partir dos dados de lançamento do LTI.
- Passá-lo de forma segura para o frontend.
Você separaria a camada de segurança da UI da mesma forma?
Separar a lógica de protocolo (Node/ltijs) da lógica de aplicação (Next.js) foi o que tornou isso viável: a camada de segurança continua chata e auditável, e a UI continua sendo um app Next.js comum, que não precisa saber nada sobre OIDC ou JWKS.
O código está no GitHub, caso você queira fazer um fork e começar a partir de algo que já resolve o handshake: lti-1.3-node-nextjs-template. Fico curioso se alguém já passou por um caso em que separar essas duas camadas assim atrapalhou mais do que ajudou.


