Package: lti
Generated from api/packages/lti
Source: api/packages/lti. The sections after the README are extracted from the code on every docs build.
README
Section titled “README”Both directions of LTI 1.3 for every tenant (ADR 0012):
- Platform: ulams launches external tools inside lessons (topic type
LtiLink), receives grades through AGS 2.0, shares the course member list through NRPS 2.0 (per tool, off by default) and lets authors pick content in the tool (Deep Linking 2.0). - Tool: Moodle, Canvas and other LMSs launch ulams courses, with grade passback and a course
picker for deep linking. Launch validation by
packbackbooks/lti-1p3-tool(Apache-2.0).
Endpoints
Section titled “Endpoints”| Who calls | Endpoint | Purpose |
|---|---|---|
| anyone | GET /api/lti/jwks, GET /.well-known/jwks.json |
Our public keys (next, active, recently retired) |
admin (lti_manage) |
GET /api/admin/lti/endpoints |
Issuer and URLs to register us elsewhere |
admin (lti_manage) |
/api/admin/lti/tools (CRUD), POST /api/admin/lti/tools/{tool}/deep-link |
Tools we launch; start deep linking into a lesson |
admin (lti_manage) |
/api/admin/lti/platforms (CRUD) |
Platforms that may launch us |
| learner | POST /api/lti/launches/{topic} |
OIDC login URL of the topic’s tool (2-minute, single-use hint) |
| tool | GET/POST /api/lti/platform/authorize |
OIDC auth: posts the signed id_token to the tool |
| tool | POST /api/lti/platform/token |
AGS access token (client credentials, JWT assertion) |
| tool | /api/lti/platform/ags/{course}/lineitems[/{id}[/scores|/results]] |
AGS line items, scores, results |
| tool | GET /api/lti/platform/nrps/{course} |
NRPS 2.0 membership container (scope contextmembership.readonly, nrps_enabled on the tool; limit, page, role; Link rel=next) |
| tool | POST /api/lti/platform/deep-links |
Deep-linking response: creates LtiLink topics |
| platform | GET/POST /api/lti/tool/login, POST /api/lti/tool/launch |
OIDC login initiation and launch |
| learner’s browser | POST /api/lti/tool/launch/verify |
Second step when the login used the platform’s storage (lti_storage_target, Client Side postMessage Storage): checks the value read from the platform against the login’s nonce |
| learner’s browser | POST /api/lti/tool/deep-link |
Returns the picked courses to the platform |
| front | POST /api/lti/tool/exchange |
One-time launch code to a Passport token |
Registering
Section titled “Registering”A tool in ulams (platform side): POST /api/admin/lti/tools with the tool’s OIDC login URL,
launch URL, optional deep-linking URL, and its JWKS URL or PEM public key. ulams issues client_id
and deployment_id; give the tool those plus the issuer, oidc_auth_url, token_url and
jwks_url from GET /api/admin/lti/endpoints.
ulams in Moodle (tool side): in Moodle add an external tool (LTI 1.3) with the tool URLs from
GET /api/admin/lti/endpoints (tool.oidc_login_url, tool.launch_url, jwks_url), then
POST /api/admin/lti/platforms with Moodle’s issuer (site URL), client ID, deployment ID, and its
auth.php, token.php and certs.php URLs. A resource link picks the course with the custom
parameter course_id=<id> (deep linking sets it), ?course=<id> on the target link URI, or the
platform’s default_course_id.
Security
Section titled “Security”- Per-tenant RSA key set, private keys encrypted with the tenant
APP_KEY, rotated monthly (ulams:lti:rotate-keys, scheduled;--initat provisioning, steplti_keys). - Login hints, deep-linking data and picker forms are signed with an
APP_KEY-derived secret and short-lived; login hints, JWT ids, OIDC state/nonce and launch codes are single use (lti_nonces). - Every inbound JWT is checked for signature,
iss,aud,exp, nonce/jti and deployment id. - Outgoing requests (JWKS, tokens, AGS) go only to public
httpsaddresses, without redirects. - Tool side: users are matched by
(platform, sub), never by e-mail; Instructor maps to tutor, nobody to admin. - Every launch is audited in
lti_launches.
Configuration
Section titled “Configuration”See src/config.php and the LTI section of docs/enviromental-variables.md.
vendor/bin/phpunit --testsuite ltiFake tools and platforms sign with in-test RSA keys (tests/Support/KeyPair.php); the tool side’s
HTTP client is replaced with an in-memory platform. tests/Feature/TenantIsolationTest.php checks
that hints, AGS tokens, deep-linking data and keys of one tenant are rejected by another; the
HTTP version against two real tenants is in packages/tenancy/tests/Integration.
Topic types
Section titled “Topic types”LtiLink
API endpoints
Section titled “API endpoints”| Method | Path | Auth | Action |
|---|---|---|---|
GET |
/api/lti/jwks |
JwksController |
|
GET |
/.well-known/jwks.json |
JwksController |
|
GET |
/api/lti/frame-origins |
FrameOriginsController |
|
GET |
/api/admin/lti/endpoints |
yes | LtiAdminController@endpoints |
GET |
/api/admin/lti/tools |
yes | LtiAdminController@tools |
POST |
/api/admin/lti/tools |
yes | LtiAdminController@storeTool |
GET |
/api/admin/lti/tools/{tool} |
yes | LtiAdminController@showTool |
PUT |
/api/admin/lti/tools/{tool} |
yes | LtiAdminController@updateTool |
DELETE |
/api/admin/lti/tools/{tool} |
yes | LtiAdminController@destroyTool |
POST |
/api/admin/lti/tools/{tool}/deep-link |
yes | LtiAdminController@deepLink |
GET |
/api/admin/lti/platforms |
yes | LtiAdminController@platforms |
POST |
/api/admin/lti/platforms |
yes | LtiAdminController@storePlatform |
GET |
/api/admin/lti/platforms/{platform} |
yes | LtiAdminController@showPlatform |
PUT |
/api/admin/lti/platforms/{platform} |
yes | LtiAdminController@updatePlatform |
DELETE |
/api/admin/lti/platforms/{platform} |
yes | LtiAdminController@destroyPlatform |
POST |
/api/lti/launches/{topic} |
yes | PlatformController@launch |
GET|POST |
/api/lti/platform/authorize |
PlatformController@oidcAuthorize |
|
POST |
/api/lti/platform/token |
PlatformController@token |
|
POST |
/api/lti/platform/deep-links |
PlatformController@deepLinks |
|
GET |
/api/lti/platform/ags/{course}/lineitems |
AgsController@index |
|
POST |
/api/lti/platform/ags/{course}/lineitems |
AgsController@store |
|
GET |
/api/lti/platform/ags/{course}/lineitems/{lineItem} |
AgsController@show |
|
PUT |
/api/lti/platform/ags/{course}/lineitems/{lineItem} |
AgsController@update |
|
DELETE |
/api/lti/platform/ags/{course}/lineitems/{lineItem} |
AgsController@destroy |
|
POST |
/api/lti/platform/ags/{course}/lineitems/{lineItem}/scores |
AgsController@scores |
|
GET |
/api/lti/platform/ags/{course}/lineitems/{lineItem}/results |
AgsController@results |
|
GET |
/api/lti/platform/nrps/{course} |
NrpsController@memberships |
|
GET|POST |
/api/lti/tool/login |
ToolController@login |
|
POST |
/api/lti/tool/launch |
ToolController@launch |
|
POST |
/api/lti/tool/launch/verify |
ToolController@verify |
|
POST |
/api/lti/tool/deep-link |
ToolController@deepLink |
|
POST |
/api/lti/tool/exchange |
ToolController@exchange |
Permissions
Section titled “Permissions”| Permission | Seeded for roles | Constant |
|---|---|---|
lti_manage |
admin | LtiPermissionsEnum::LTI_MANAGE |
Settings
Section titled “Settings”Registered with AdministrableConfig::registerConfig (editable in the admin panel under Configuration → Settings).
None.
Events
Section titled “Events”None.
Artisan commands
Section titled “Artisan commands”| Command | Description | Signature |
|---|---|---|
ulams:lti:rotate-keys |
Rotate the tenant LTI signing keys: next becomes active, a new next key is generated, old retired keys are removed | ulams:lti:rotate-keys {--init : only create the key set when missing (tenant provisioning)} |
Scheduled jobs
Section titled “Scheduled jobs”| Kind | Target | Frequency |
|---|---|---|
| command | 'ulams:lti:rotate-keys' |
monthlyOn(1, '03:00') |
Environment variables read
Section titled “Environment variables read”LTI_ACCESS_TOKEN_TTL, LTI_ALLOW_INSECURE_URLS, LTI_CODE_TTL, LTI_HTTP_TIMEOUT, LTI_ID_TOKEN_TTL, LTI_ISSUER, LTI_JWKS_CACHE_TTL, LTI_KEY_BITS, LTI_LOGIN_HINT_TTL, LTI_RETIRED_KEY_GRACE_DAYS, LTI_STATE_TTL, LTI_TOOL_LANDING_URL