Saltar al contenido
Alberto LaraAnálisis y arquitectura EdTech
Ir a la web

Arquitectura de plataformas · Operación· 17 min

Operar LTI 1.3: rotación de claves, trazas y fallos explicables

Una guía operativa para localizar el fallo, rotar claves sin interrumpir lanzamientos y recuperar la integración sin relajar la frontera de confianza.

AL Alberto Lara Hernández ·
Una operación LTI atraviesa fases observables y una rotación de claves solapada sin interrumpir la frontera de confianza.
Una operación LTI atraviesa fases observables y una rotación de claves solapada sin interrumpir la frontera de confianza.

A las 9:02 un profesor informa de que la actividad externa muestra una pantalla en blanco. A las 9:05 soporte confirma que «LTI falla». El panel solo enseña respuestas 500 y el equipo no sabe si el problema está en Moodle, en la herramienta, en una clave, en el reloj del servidor, en una cookie bloqueada o en la devolución de una nota. Reiniciar parece más rápido que diagnosticar.

Este desconcierto procede del diseño operativo, no del estándar.

Para considerar lista una integración LTI 1.3, no basta con que un lanzamiento de prueba llegue a la herramienta. El equipo también debe poder localizar cada fallo en una fase y explicar por qué se rechazó una petición. Además, debe rotar claves sin un corte coordinado, limitar los reintentos y recuperar el servicio sin aceptar mensajes dudosos.

En LTI 1.3 en producción desarrollo la frontera de confianza: registro, lanzamiento, identidad y servicios. Esta guía empieza en el paso siguiente y trata LTI como una máquina de estados observable. Reducirlo a un controlador que «valida el JWT» y devuelve el mismo error para todo impediría operarlo con criterio.

La observabilidad exige conservar la decisión que tomó cada fase sin copiar los datos personales del token.

El incidente debe caer en una fase

LTI combina mensajes que pasan por el navegador y servicios entre servidores. Los mensajes originados por la plataforma —como un lanzamiento o LtiDeepLinkingRequest— llegan a la herramienta como un id_token firmado. Los originados por la herramienta —como LtiDeepLinkingResponse— regresan también como JWT firmado, pero en el parámetro de formulario JWT. Para AGS y NRPS, en cambio, la herramienta obtiene un access_token mediante una aserción propia y llama al servicio directamente. Confundir estos recorridos produce diagnósticos imposibles, como buscar una cookie para explicar un 403 del servicio de calificaciones.

Dividiría la operación en ocho fases:

FaseEntrada esperadaEvidencia de éxitoFallos que deben distinguirse
RegistroEmisor, cliente, despliegue y direcciones acordadasVersión activa identificableEmisor desconocido, cliente ambiguo, despliegue retirado
Inicio OIDCPetición no firmada de la plataformaTransacción de un solo uso creadaRegistro no resoluble, destino no permitido, inicio repetido
AutorizaciónRedirección al punto registradostate y nonce vinculados al navegadorEstado perdido, retorno distinto, transacción caducada
Lanzamientoid_token mediante form_postFirma, campos del token y mensaje válidosFirma, audiencia, tiempo, nonce, despliegue o tipo incorrectos
Política y sesiónLanzamiento ya verificadoRecurso permitido y sesión local creadaRol insuficiente, recurso retirado, cookie o marco bloqueado
Token de servicioAserción de la herramientaToken con los alcances concedidosClave propia, audiencia, jti, cliente o alcance rechazados
Servicio LTIPetición entre servidores a AGS o NRPSResultado de dominio confirmado401, 403, 404, 409, 429, 5xx, tiempo de espera o duplicado
Deep LinkingLtiDeepLinkingRequest, selección y LtiDeepLinkingResponse mediante el navegadorRetorno firmado validado y recursos incorporadosFirma, data, deployment_id, retorno o contenido rechazados

La fase determina quién puede resolver el incidente y qué acción es segura. Si el state no existe, no se vuelve a consultar JWKS. Cuando la firma es válida pero el despliegue está retirado, refrescar claves no sirve. Del mismo modo, si AGS responde 403, relajar la validación del lanzamiento sería peligroso y además irrelevante.

La interfaz para la persona usuaria puede agrupar errores para no revelar detalles. La traza interna, en cambio, necesita un código estable, por ejemplo LTI-LAUNCH-AUD-MISMATCH. También debe explicar la fase, la decisión y la siguiente comprobación.

El registro también tiene historial

Una tabla con la configuración actual explica cómo debería funcionar LTI ahora, pero no cómo estaba configurado a las 9:02.

Modelaría el registro como un contrato versionado. Una versión incluye el issuer, el client_id, el punto de autorización, el punto de token, el jwks_uri, los algoritmos admitidos, las direcciones de inicio y retorno y sus fechas de vigencia. Cada deployment_id mantiene además propietario, estado, capacidades, política de datos y fecha de retirada.

Aunque no hace falta conservar secretos antiguos, sí es necesario registrar qué versión se usó para resolver una petición. También debe quedar claro por qué dejó de estar vigente. Cuando alguien cambia una dirección en producción, la modificación crea una versión y un evento de auditoría; no sobrescribe la explicación del día anterior.

Tres reglas eliminan mucha ambigüedad:

  1. El registro se resuelve por el emisor y el cliente acordados, no por el dominio visible en el navegador.
  2. El despliegue se autoriza después de validar el mensaje; conocer un issuer no activa cualquier instalación de la herramienta.
  3. Las direcciones de confianza proceden del registro. Un jku, x5u o destino recibido dentro de un JWT no modifica de dónde se obtienen claves ni adónde se redirige.

La última regla aplica una recomendación del BCP de JWT: los campos y cabeceras recibidos no son configuración de confianza. El kid sirve para localizar una clave dentro del conjunto asociado previamente al emisor; no convierte el token en un directorio de claves.

Una traza útil no contiene el lanzamiento

El recorrido necesita un identificador de correlación creado al inicio y propagado hasta la sesión local. Para servicios asíncronos puede nacer otro identificador enlazado al primero. Ninguno debería depender del correo, el sub, el curso o el recurso.

Un evento técnico podría tener esta forma:

{
  "trace_id": "01K0…",
  "phase": "launch.validation",
  "outcome": "rejected",
  "reason": "LTI-LAUNCH-NONCE-REPLAY",
  "registration_version": "reg-42:v7",
  "deployment_ref": "dep:8f51…",
  "message_type": "LtiResourceLinkRequest",
  "kid": "platform-2026-07",
  "algorithm": "RS256",
  "received_at": "2026-07-28T09:02:14Z",
  "duration_ms": 18
}

deployment_ref puede ser una referencia interna o un valor seudonimizado si el identificador real revela a la institución. El evento no guarda el token, el nombre, el correo, los roles completos, los parámetros de inicio ni el access_token. Si una investigación necesita un dato adicional, debe existir una vía controlada y temporal para obtenerlo; no una copia permanente en cada índice de registros.

Registraría por fase:

  • versión de configuración elegida y resultado de la resolución;
  • creación, consumo, caducidad o repetición de state y nonce, sin guardar sus valores en claro;
  • kid, algoritmo y origen de la clave usada;
  • validaciones de emisor, audiencia, tiempo y campos LTI como resultados discretos;
  • decisión de política sobre despliegue, tipo de mensaje y recurso;
  • resultado HTTP y semántico de los servicios, alcance solicitado y concedido;
  • reintento, demora, circuito abierto y resultado final.

Así se puede buscar «todas las firmas rechazadas tras la versión v7» sin exponer lanzamientos. También se evita la falsa solución de activar un nivel de registro detallado durante la incidencia y descubrir después que ha volcado credenciales y datos académicos.

Un kid desconocido no autoriza una búsqueda sin límites

La guía de implementación de 1EdTech establece que la plataforma publica sus claves mediante un JWK Set, que cada kid identifica una clave inmutable y que la herramienta debe volver a consultar el jwks_uri cuando no encuentra ese identificador en caché. Esa recuperación permite rotar las claves, siempre que se aplique con límites.

El algoritmo operativo debería ser:

  1. buscar el kid en la caché asociada al issuer y al jwks_uri registrados;
  2. si no existe, comprobar la caché negativa, el intervalo mínimo desde la última recarga y el presupuesto de actualizaciones del emisor;
  3. cuando se permita actualizar, coordinar una única descarga para ese emisor, de modo que cien lanzamientos simultáneos no produzcan cien peticiones;
  4. consultar solo la dirección registrada, con tiempo de espera, tamaño y formato acotados;
  5. sustituir la vista de claves únicamente si el conjunto es válido;
  6. volver a buscar el kid una vez;
  7. si sigue sin aparecer, rechazar el lanzamiento con un motivo específico y conservar brevemente ese identificador en la caché negativa.

La coordinación evita la estampida concurrente, pero no basta frente a identificadores aleatorios enviados de forma secuencial. El intervalo mínimo, el presupuesto por emisor y una caché negativa de corta duración limitan esa amplificación sin impedir una rotación con solape. Tampoco seguiría una dirección incluida en el token: además de romper el registro de confianza, abre la puerta a peticiones del servidor hacia destinos no autorizados.

Hay dos estados que no deben confundirse. No pude actualizar JWKS significa que la fuente registrada no respondió o devolvió un conjunto inválido. Actualicé JWKS y el kid no existe significa que el mensaje cita una clave que la plataforma no publica. Ambos terminan en rechazo, pero el primero puede ser una indisponibilidad temporal y el segundo apunta a una configuración o firma incorrectas.

Disponibilidad no es aceptar con la última clave Si el token cita un kid desconocido, probar todas las claves antiguas o ignorar la cabecera puede convertir una incidencia en una ruptura de la frontera de confianza. La recuperación conserva las mismas validaciones; solo actualiza el conjunto autorizado.

La rotación necesita solape y una reversión preparada

Una rotación segura crea un periodo durante el que emisores y verificadores aceptan el cambio. Durante ese intervalo todavía debe poder explicarse qué clave se usó; sustituirla a una hora acordada no basta.

Para rotar una clave de la herramienta —la que firma aserciones para obtener tokens o respuestas de Deep Linking— seguiría este orden:

  1. generar la clave nueva en el gestor autorizado y asignarle un kid nuevo;
  2. publicar la clave pública nueva junto a la anterior;
  3. comprobar desde fuera que el JWK Set correcto se propaga a todos los entornos y cachés relevantes;
  4. empezar a firmar con la clave nueva en un ámbito controlado;
  5. observar solicitudes de token, rechazos por kid y firmas todavía emitidas con la anterior;
  6. extender el cambio al resto de despliegues;
  7. retirar la clave pública anterior cuando haya terminado la ventana real de validez y no se observen usos legítimos;
  8. revocar el material privado anterior según la política de custodia.

La ventana no debería copiarse de una receta. Se calcula con la vida máxima de los mensajes emitidos, el margen de reloj admitido, la caché efectiva y el tiempo de propagación observado. La propia guía de seguridad de 1EdTech recomienda conservar la clave pública anterior durante el solape. Conservarla para siempre tampoco es rotación: amplía el inventario de claves aceptadas y dificulta saber cuál debería estar en uso.

La reversión debe estar decidida antes del paso cuatro. Mientras ambas claves públicas siguen publicadas, volver a firmar con la anterior puede restaurar servicio sin redistribuir configuración. Si la clave antigua se ha comprometido, esa reversión está prohibida; el incidente exige revocación y coordinación, no disponibilidad a cualquier precio.

La plataforma rota su propia clave con el mismo principio de solape, pero la herramienta no controla el calendario. Por eso debe refrescar ante un kid desconocido y medir cuánto tarda en reconocer la clave nueva.

El lanzamiento LTI atraviesa registro, inicio OIDC, validación, política y sesión. Debajo, una rotación publica la clave nueva junto a la anterior, cambia la firma y retira la anterior después de verificar la propagación.
Dos recorridos que deben poder explicarse. La traza localiza la fase del fallo; el solape permite cambiar de clave sin abrir una excepción de validación.

Los servicios se recuperan fuera del camino del navegador

El id_token del lanzamiento no es el token para llamar a AGS o NRPS. La herramienta firma una aserción, solicita un access_token al punto registrado y pide únicamente los alcances que necesita. El fallo puede estar en esa autenticación o en la operación posterior; conviene medirlas por separado.

No bloquearía el lanzamiento porque NRPS tarde en responder o porque una escritura de calificación necesite reintentarse. Una vez que el navegador confirma la entrada de la persona, las operaciones entre servidores pueden pasar a una cola duradera. Para ello, el producto debe admitir la ejecución diferida. Cada trabajo conserva el ámbito, el alcance, la operación y la clave de idempotencia del dominio, además del plazo después del cual ya no tiene sentido ejecutarlo.

La política de reintentos depende del resultado:

ResultadoDecisión inicialCondición para reintentar
Tiempo de espera o red interrumpidaResultado desconocidoConsultar estado o usar idempotencia antes de repetir una mutación
401Credencial no aceptadaRenovar una vez si el token caducó; no repetir si falla la aserción
403Operación no autorizadaNo reintentar sin un cambio de alcance, despliegue o política
404Recurso o dirección no válidoVerificar identificador y versión de configuración
409Conflicto de estadoReconciliar el estado actual antes de decidir
429Límite del proveedorRespetar Retry-After y presupuesto del trabajo
5xxFallo temporal posibleReintento acotado con espera creciente y variación aleatoria

«POST falló, repetir» no es una estrategia. Si la plataforma procesó la nota y la respuesta se perdió, una segunda petición puede duplicar o sobrescribir una operación según el servicio. El adaptador necesita conocer la semántica, comparar el estado remoto cuando sea posible y registrar qué resultado considera definitivo.

Las métricas deben conducir a una decisión

Un panel con volumen total de lanzamientos no avisa de que una sola institución lleva veinte minutos sin poder entrar. Mediría tasas y latencias por fase, producto, versión de registro y despliegue seudonimizado, con cuidado de no convertir identificadores de alta cardinalidad en etiquetas de la plataforma de métricas.

Las señales mínimas serían:

  • lanzamientos iniciados, aceptados y rechazados por código de motivo;
  • transacciones OIDC creadas, caducadas y repetidas;
  • aciertos de caché JWKS, actualizaciones, errores y kid ausentes tras actualizar;
  • uso de cada kid para firmar y validar durante una rotación;
  • creación de sesión y errores específicos de navegador o marco;
  • solicitudes de token por alcance y resultado;
  • latencia, códigos, reintentos y agotamiento de trabajos de AGS y NRPS;
  • antigüedad de la operación pendiente más vieja;
  • versiones de configuración activadas, revertidas y retiradas.

Alertaría sobre síntomas que tienen una respuesta. Un aumento de state perdido afecta al flujo del navegador; un kid desconocido concentrado en un emisor apunta a rotación o configuración; un 403 en AGS para un único despliegue obliga a revisar alcances; una cola de notas envejecida exige frenar o ampliar la recuperación. «Más errores LTI» no indica quién debe hacer qué.

Los objetivos de servicio también se separan. La tasa de lanzamientos aceptados mide el acceso interactivo, mientras que el tiempo transcurrido hasta confirmar una calificación mide una operación diferida. Si se mezclan, miles de lanzamientos pueden ocultar cien notas atascadas.

El banco de pruebas debe atacar las suposiciones

Antes de declarar operable la integración ejecutaría al menos estas pruebas negativas en un entorno representativo:

  1. emisor no registrado y emisor válido con cliente incorrecto;
  2. state ausente, caducado, de otro navegador y consumido dos veces;
  3. nonce incorrecto y repetido;
  4. token con exp caducado; iat ausente, no numérico, futuro o fuera de la ventana de frescura definida; y, cuando aparezca, nbf fuera del margen admitido;
  5. algoritmo no permitido, firma alterada y clave que pertenece a otro emisor;
  6. aud incorrecto y audiencia múltiple con azp no autorizado;
  7. kid nuevo disponible tras actualizar JWKS;
  8. kid inexistente incluso después de actualizar;
  9. JWKS lento, no disponible, demasiado grande o con formato inválido;
  10. deployment_id desconocido, retirado o asociado a otro registro;
  11. tipo o versión de mensaje incorrectos y campo obligatorio ausente;
  12. destino firmado no permitido y recurso retirado;
  13. cookies de sesión bloqueadas en un iframe y lanzamiento en ventana nueva;
  14. aserción de servicio repetida, audiencia incorrecta o alcance denegado;
  15. AGS o NRPS con 401, 403, 404, 409, 429, 5xx, tiempo de espera y respuesta perdida;
  16. dos trabajadores recuperando el mismo trabajo y llegada fuera de orden.

Cada prueba define respuesta visible, código interno, cambio de estado, métrica esperada y ausencia de datos sensibles en los registros. No basta con comprobar que devuelve 400. La prueba debe demostrar que el equipo sabría qué hacer con ese 400.

La certificación de 1EdTech ayuda a comprobar conformidad. No sustituye este banco: una implementación puede cumplir el intercambio y seguir sin tener rotación ensayada, cola duradera, trazas utilizables o una guía operativa propia.

La guía operativa empieza por contener el fallo

Cuando llega una incidencia, seguiría esta secuencia:

  1. Localizar la fase. Obtener un trace_id y el primer resultado que se apartó del recorrido esperado.
  2. Acotar el alcance. Determinar si afecta a un emisor, cliente, despliegue, versión, navegador, kid, servicio o a todos.
  3. Detener daño adicional. Pausar reintentos que duplican operaciones, limitar una cola creciente o retirar una versión defectuosa. No desactivar firma, audiencia, state o nonce.
  4. Comparar cambios. Revisar versiones de registro, rotaciones, reloj, despliegues y publicación de JWKS en la ventana del incidente.
  5. Reproducir el rechazo. Usar un vector sin datos personales que produzca el mismo código de motivo.
  6. Recuperar con una acción reversible. Revertir configuración o firma cuando la clave anterior siga siendo segura; reanudar trabajos con idempotencia; desactivar solo el despliegue afectado si el modelo lo permite.
  7. Verificar el resultado. Confirmar lanzamiento, sesión y servicios por separado y observar la cola hasta vaciarla.
  8. Cerrar la explicación. Guardar cronología, causa, detección, alcance, acción, evidencia de recuperación y prueba que evitará la regresión.

La recuperación tiene condiciones de parada. Si no se puede vincular la clave al emisor registrado, el lanzamiento se rechaza. Cuando una aserción puede haberse repetido, no se concede el token solo para recuperar disponibilidad. Si una escritura tiene un resultado desconocido y carece de reconciliación, se detiene antes de duplicarla. El modo degradado puede ocultar una función auxiliar, pero nunca inventa confianza.

Doce pruebas antes de llamar a LTI un servicio

Consideraría lista la operación cuando el equipo puede demostrar:

  1. configuración versionada y reversible por registro y despliegue;
  2. códigos de rechazo estables para cada fase;
  3. state, nonce y aserciones de un solo uso probados contra repetición;
  4. claves ligadas a emisores y algoritmos permitidos explícitamente;
  5. actualización JWKS ante kid desconocido con límites y control de concurrencia;
  6. rotación propia ensayada con solape, observación y reversión;
  7. trazas correlacionadas sin tokens ni datos personales;
  8. sesiones de navegador diagnosticables sin confundirlas con firma o política;
  9. servicios desacoplados del lanzamiento cuando el proceso lo admite;
  10. reintentos acotados, reconciliación e idempotencia para mutaciones;
  11. paneles y alertas vinculados a acciones concretas;
  12. un simulacro en el que otra persona siga la guía operativa y recupere el servicio.

El último punto revela documentación que solo entiende quien escribió la integración. Una guía operativa no se valida leyéndola, sino observando si una persona de guardia puede usarla bajo presión sin llamar al autor para interpretar cada paso.

Operar es conservar la frontera durante el fallo

LTI 1.3 sustituye secretos compartidos por un modelo más explícito de confianza. Esa mejora desaparece si, ante el primer incidente, el equipo prueba todas las claves, acepta cualquier audiencia, reintenta sin límite o vuelca tokens para entender qué ocurre.

Una operación madura divide el recorrido, conserva qué configuración decidió cada petición, rechaza con motivos precisos, rota mediante solape, aísla los servicios del navegador y convierte los fallos esperados en pruebas repetibles.

El objetivo consiste en que cada fallo tenga un ámbito, una explicación y una recuperación que no obligue a debilitar aquello que la integración pretendía proteger.

AL
Alberto Lara Hernández
Director tecnológico y arquitecto de sistemas de aprendizaje

Más de 22 años construyendo y evolucionando plataformas de aprendizaje que tienen que operar de verdad.

Seguir leyendo