Поддержка LTI-интеграций в LMS
LTI (Learning Tools Interoperability) — стандарт IMS Global для встраивания внешних образовательных инструментов в LMS. Через LTI преподаватель настраивает один раз подключение к Kahoot, Phet Simulations, Coursera for Campus, Microsoft Teams — и студенты запускают их прямо из LMS без отдельной регистрации. Однако на практике LTI-интеграция часто превращается в головную боль: ошибки конфигурации, несовместимость версий (1.1 vs 1.3), потеря сессии при OIDC flow, проблемы с передачей оценок через Outcomes. Неправильная настройка JWKS может привести к тому, что инструмент не примет JWT — и вся интеграция перестаёт работать. Наши инженеры с 10-летним опытом в EdTech решают эти проблемы под ключ — от аудита текущей LMS до подключения любого LTI-инструмента. За годы работы мы накопили библиотеку типовых ошибок и готовых шаблонов, что сокращает время разработки на 30-50%. Мы гарантируем совместимость с любым LTI-совместимым инструментом, сертифицированным IMS Global.
Согласно спецификации IMS Global LTI 1.3, использование OAuth 2.0 и JWKS обеспечивает более высокий уровень безопасности по сравнению с LTI 1.1.
Почему LTI 1.3 лучше LTI 1.1?
LTI 1.1 использует OAuth 1.0 — устаревший протокол с хрупкими подписями. LTI 1.3 базируется на OAuth 2.0 и OpenID Connect: безопаснее, прозрачнее и проще в отладке. JWT-токены с JWKS-ключами исключают подделку запросов. Многие поставщики (Coursera, Zoom, Adobe) переходят на LTI 1.3 — без него вы не сможете подключить современные инструменты.
| Характеристика | LTI 1.1 | LTI 1.3 |
|---|---|---|
| Протокол | OAuth 1.0 | OAuth 2.0 + OIDC |
| Подпись | HMAC-SHA1 | RSA/JWT |
| Управление ключами | Shared secret (статика) | JWKS (ротация) |
| Безопасность | Низкая | Высокая |
| Время на интеграцию | 2-3 дня | 3-5 дней |
Как настроить LTI 1.3 в вашей LMS?
LMS выступает в роли Platform (по IMS). Процесс инициализации включает OIDC Login Request и генерацию JWT. Ниже — типовой код с библиотекой ltijs:
import { Provider } from 'ltijs'; // npm install ltijs
// Настройка LMS как LTI Platform
Provider.setup(
process.env.LTI_ENCRYPTION_KEY!, // 32+ символов для шифрования cookies
{
url: process.env.DATABASE_URL!,
plugin: require('ltijs-postgresql'), // адаптер для PostgreSQL
},
{
cookies: { secure: true, sameSite: 'None' },
devMode: process.env.NODE_ENV !== 'production',
}
);
// Регистрация внешнего инструмента
await Provider.registerPlatform({
url: 'https://tool.example.com',
name: 'Kahoot Integration',
clientId: process.env.KAHOOT_CLIENT_ID!,
authenticationEndpoint: 'https://tool.example.com/lti/auth',
accesstokenEndpoint: 'https://tool.example.com/lti/token',
authConfig: {
method: 'JWK_SET',
key: 'https://tool.example.com/.well-known/jwks.json',
},
});
// Обработчик запуска инструмента
Provider.onConnect(async (token, req, res) => {
const { email, name, role } = token.userInfo;
const contextId = token.platformContext.context?.id;
// Проверить/создать пользователя во внешнем инструменте
res.json({ token: token.jwt });
});
await Provider.deploy({ serverless: true });
LTI 1.1 — для устаревших инструментов
Часть поставщиков (Phet, некоторые тесты) всё ещё использует LTI 1.1 с OAuth 1.0 подписью:
import oauth from 'oauth-signature';
function launchLti11(
launchUrl: string,
consumerKey: string,
consumerSecret: string,
params: Record<string, string>
): { url: string; method: 'POST'; body: string } {
const baseParams: Record<string, string> = {
lti_message_type: 'basic-lti-launch-request',
lti_version: 'LTI-1p0',
oauth_callback: 'about:blank',
oauth_consumer_key: consumerKey,
oauth_nonce: crypto.randomUUID().replace(/-/g, ''),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: String(Math.floor(Date.now() / 1000)),
oauth_version: '1.0',
...params,
};
const signature = oauth.generate('POST', launchUrl, baseParams, consumerSecret, '');
baseParams.oauth_signature = signature;
const body = new URLSearchParams(baseParams).toString();
return { url: launchUrl, method: 'POST', body };
}
// Страница запуска LTI инструмента
app.get('/courses/:courseId/tools/:toolId/launch', authenticate, async (req, res) => {
const tool = await db.ltiTools.findById(req.params.toolId);
const enrollment = await db.enrollments.findByCourseAndUser(
req.params.courseId, req.user.id
);
if (tool.version === '1.1') {
const launch = launchLti11(tool.launch_url, tool.consumer_key, tool.consumer_secret, {
resource_link_id: `${req.params.courseId}-${req.params.toolId}`,
resource_link_title: tool.name,
user_id: req.user.id,
lis_person_name_full: req.user.name,
lis_person_contact_email_primary: req.user.email,
roles: enrollment.role === 'instructor' ? 'Instructor' : 'Student',
context_id: req.params.courseId,
context_title: enrollment.courseTitle,
});
// Авто-сабмит форму через HTML
res.send(`
<!DOCTYPE html>
<html>
<body>
<form id="lti" method="POST" action="${launch.url}">
${Object.entries(Object.fromEntries(new URLSearchParams(launch.body)))
.map(([k, v]) => `<input type="hidden" name="${k}" value="${v}" />`)
.join('\n')}
</form>
<script>document.getElementById('lti').submit();</script>
</body>
</html>
`);
}
});
Хранение конфигурации инструментов
CREATE TABLE lti_tools (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
course_id UUID REFERENCES courses(id),
name VARCHAR(255) NOT NULL,
version VARCHAR(10) NOT NULL, -- '1.1' | '1.3'
-- LTI 1.1
launch_url TEXT,
consumer_key VARCHAR(255),
consumer_secret TEXT,
-- LTI 1.3
client_id VARCHAR(255),
platform_id VARCHAR(255),
deployment_id VARCHAR(255),
oidc_auth_url TEXT,
jwks_url TEXT,
-- Настройки
open_in_new_tab BOOLEAN DEFAULT false,
custom_params JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT now()
);
Получение результатов через LTI Outcomes (1.1)
// Инструмент может отправить оценку обратно в LMS
app.post('/lti/grade', async (req, res) => {
const { sourcedId, score, action } = parseLtiOutcomesXml(req.body);
// sourcedId содержит userId и resourceLinkId
const [userId, resourceId] = parseLisResultSourcedId(sourcedId);
await db.ltiGrades.upsert({
userId,
resourceId,
score: Number(score),
receivedAt: new Date(),
});
// Обновить прогресс курса
await updateCourseProgress(userId, resourceId, Number(score));
res.type('application/xml').send(`
<?xml version="1.0" encoding="UTF-8"?>
<imsx_POXEnvelopeResponse xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
<imsx_POXHeader>
<imsx_POXResponseHeaderInfo>
<imsx_version>V1.0</imsx_version>
<imsx_messageIdentifier>${crypto.randomUUID()}</imsx_messageIdentifier>
<imsx_statusInfo>
<imsx_codeMajor>success</imsx_codeMajor>
<imsx_severity>status</imsx_severity>
</imsx_statusInfo>
</imsx_POXResponseHeaderInfo>
</imsx_POXHeader>
<imsx_POXBody><replaceResultResponse /></imsx_POXBody>
</imsx_POXEnvelopeResponse>
`);
});
Что входит в работу по LTI-интеграции?
- Аудит текущей архитектуры LMS: выявление узких мест (N+1 запросы, потери сессий), оценка совместимости с нужными инструментами.
- Проектирование схемы хранения конфигураций инструментов (таблица lti_tools с поддержкой обеих версий LTI).
- Реализация LTI 1.1 и/или LTI 1.3 launch flow на вашем стеке (Node.js, Python, PHP, Ruby — любой).
- Интеграция с внешним инструментом: регистрация, тестовый запуск, передача оценок (Outcomes).
- Написание документации для администраторов и инструкторов по добавлению новых инструментов.
- Обучение команды: как отлаживать ошибки, обновлять ключи, работать с JWKS.
- Пост-релизная поддержка в течение 30 дней: исправление багов, консультации.
Часто встречающиеся ошибки при LTI-интеграции
- Несоответствие типов пользователей: путают роль Instructor и Student в JWT.
- Ошибки JWKS: ключи истекают или не обновляются.
- Проблемы с CORS: внешние инструменты блокируют запросы.
- Неправильная настройка OIDC Login Request: неверный redirect URI.
Сроки и стоимость
LTI 1.1 интеграция (Consumer + Launch + Grades) — 3–5 дней. LTI 1.3 с OIDC flow — 1 неделя. Поддержка обеих версий + UI управления инструментами — 2 недели. Стоимость рассчитывается индивидуально, а использование готовых шаблонов позволяет существенно сократить бюджет.
Наш опыт и гарантии
Кейс: интеграция Kahoot в LMS университета. Потребовалась поддержка LTI 1.3, так как Kahoot перешёл на новый стандарт. Мы реализовали OIDC flow, настроили JWKS и протестировали передачу оценок. В результате студенты получили единый интерфейс без повторной аутентификации. Получите консультацию по интеграции LTI уже сегодня. Свяжитесь с нами — оценим вашу задачу и предложим решение в течение 2 рабочих дней. Обращайтесь, чтобы повысить удобство вашей LMS.







