{"id":31946380,"url":"https://github.com/lti-tool/lti-tool","last_synced_at":"2026-05-27T03:10:47.621Z","repository":{"id":317685458,"uuid":"1068432318","full_name":"lti-tool/lti-tool","owner":"lti-tool","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-26T11:44:20.000Z","size":777,"stargazers_count":4,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-01T22:29:22.449Z","etag":null,"topics":["aws-lambda","cloudflare-workers","dynamodb","edtech","hono","lti","lti-1-3","lti-advantage","serverless","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lti-tool.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-02T11:28:44.000Z","updated_at":"2026-03-19T19:21:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"d6a8c9ea-7a13-42d2-a589-9aa334ffc112","html_url":"https://github.com/lti-tool/lti-tool","commit_stats":null,"previous_names":["lti-tool/lti-tool"],"tags_count":61,"template":false,"template_full_name":null,"purl":"pkg:github/lti-tool/lti-tool","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lti-tool%2Flti-tool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lti-tool%2Flti-tool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lti-tool%2Flti-tool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lti-tool%2Flti-tool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lti-tool","download_url":"https://codeload.github.com/lti-tool/lti-tool/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lti-tool%2Flti-tool/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31914458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"online","status_checked_at":"2026-04-17T02:00:06.879Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-lambda","cloudflare-workers","dynamodb","edtech","hono","lti","lti-1-3","lti-advantage","serverless","typescript"],"created_at":"2025-10-14T11:15:44.963Z","updated_at":"2026-05-27T03:10:47.615Z","avatar_url":"https://github.com/lti-tool.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./media/logo.png\" alt=\"LTI Tool\" /\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@lti-tool/core\"\u003e\u003cimg alt=\"npm\" src=\"https://img.shields.io/npm/dm/%40lti-tool%2Fcore?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/lti-tool/lti-tool/actions/workflows/ci.yml\"\u003e\u003cimg alt=\"Build status\" src=\"https://img.shields.io/github/actions/workflow/status/lti-tool/lti-tool/release.yml\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# LTI Tool\n\n\u003cp align=\"center\"\u003eModern LTI 1.3 toolkit, built for TypeScript.\u003c/p\u003e\n\n## Why This Library?\n\nThe first **serverless-native** LTI 1.3 library for Node.js. Built for modern cloud architectures with pluggable storage and framework adapters.\n\n**Key features**\n\n- **Serverless-first** - Optimized for AWS Lambda, Cloudflare Workers\n- **Pluggable storage** - Memory, DynamoDB, PostgreSQL, MySQL, Cloudflare D1\n- **Modern frameworks** - Hono (primary), Express/Fastify (planned)\n- **Security-focused** - JWT verification, nonce validation, replay attack prevention\n- **Performance** - 6.5ms average execution time, scales to zero\n- **Cost-effective** - Under $0.001 per 1000 LTI launches\n\n**Fully Implemented LTI 1.3 Specification:**\n\n- ✅ OIDC Authentication Flow\n- ✅ Assignment and Grade Services (AGS) - Score submission, line items, results\n- ✅ Names and Role Provisioning Services (NRPS) - Member roster access\n- ✅ Deep Linking - Content selection and placement\n- ✅ Dynamic Registration - Automated tool registration\n- ✅ Security - JWT verification, nonce validation, replay attack prevention\n\n### Future Releases\n\n- **Examples Repository** - Comprehensive example implementations\n- **Framework Support** - Express, Fastify, Astro, React, Angular\n\n## Documentation\n\n- [API Reference](https://docs.lti-tool.dev) - Complete API documentation\n\n## Hono Quick Start\n\nCreate a new Hono app\n\n```bash\nnpm create hono@latest\n```\n\nInstall the packages\n\n```bash\nnpm install @lti-tool/core @lti-tool/hono @lti-tool/memory\n```\n\nCreate a minimal Hono powered LTI tool\n\n```typescript\nimport { Hono } from 'hono';\nimport { LTITool } from '@lti-tool/core';\nimport {\n  jwksRouteHandler,\n  launchRouteHandler,\n  loginRouteHandler,\n  secureLTISession,\n} from '@lti-tool/hono';\nimport { MemoryStorage } from '@lti-tool/memory';\n\n// Generate keypair (use proper key management in production)\nconst keyPair = await crypto.subtle.generateKey(\n  {\n    name: 'RSASSA-PKCS1-v1_5',\n    modulusLength: 2048,\n    publicExponent: new Uint8Array([1, 0, 1]),\n    hash: 'SHA-256',\n  },\n  true,\n  ['sign', 'verify'],\n);\n\nconst ltiConfig = {\n  stateSecret: new TextEncoder().encode('your-secret-key'),\n  keyPair,\n  storage: new MemoryStorage(),\n};\n\nconst ltiTool = new LTITool(ltiConfig);\n\n// Add your LMS configuration\nconst clientId = await ltiTool.addClient({\n  name: 'Moodle Sandbox',\n  clientId: 'your-client-id-from-moodle',\n  iss: 'https://sandbox.moodledemo.net',\n  jwksUrl: 'https://sandbox.moodledemo.net/mod/lti/certs.php',\n  authUrl: 'https://sandbox.moodledemo.net/mod/lti/auth.php',\n  tokenUrl: 'https://sandbox.moodledemo.net/mod/lti/token.php',\n});\n\nawait ltiTool.addDeployment(clientId, {\n  deploymentId: 'your-deployment-id-from-moodle',\n  name: 'Default Deployment',\n});\n\nconst app = new Hono();\n\n// Add LTI routes\napp.get('/lti/jwks', jwksRouteHandler(ltiConfig));\napp.post('/lti/launch', launchRouteHandler(ltiConfig));\napp.post('/lti/login', loginRouteHandler(ltiConfig));\n\n// Protect routes with LTI session\napp.use('/protected/*', secureLTISession(ltiConfig));\n\napp.get('/protected/content', (c) =\u003e {\n  const session = c.get('ltiSession');\n  return c.json({ message: `Hello ${session.user.name}` });\n});\n```\n\n## Performance\n\nOptimized for serverless with impressive performance metrics\n\n| Operation         | Execution Time |\n| ----------------- | -------------- |\n| Login/JWKS        | 3-5ms          |\n| Launch (heaviest) | 12-15ms        |\n| **Average**       | **6.5ms**      |\n\n## Architecture\n\n### Packages\n\n| Package                                         | Description                   | Use Case                       |\n| ----------------------------------------------- | ----------------------------- | ------------------------------ |\n| [`@lti-tool/core`](./packages/core)             | Core LTI 1.3 implementation   | Required for all setups        |\n| [`@lti-tool/hono`](./packages/hono)             | Hono framework integration    | Serverless APIs                |\n| [`@lti-tool/memory`](./packages/memory)         | In-memory storage adapter     | Development and testing        |\n| [`@lti-tool/dynamodb`](./packages/dynamodb)     | DynamoDB storage adapter      | Production AWS deployments     |\n| [`@lti-tool/postgresql`](./packages/postgresql) | PostgreSQL storage adapter    | Production SQL deployments     |\n| [`@lti-tool/mysql`](./packages/mysql)           | MySQL storage adapter         | Production SQL deployments     |\n| [`@lti-tool/d1`](./packages/d1)                 | Cloudflare D1 storage adapter | Cloudflare Workers deployments |\n\n### Storage Adapters\n\nPluggable storage system supports multiple backends\n\n- **Memory** - Development and testing\n- **DynamoDB** - Production AWS (with caching)\n- **PostgreSQL** - Production SQL deployments\n- **MySQL** - Production SQL deployments\n- **Cloudflare D1** - Cloudflare Workers deployments\n- **Custom** - Implement the `LTIStorage` interface\n\n### Framework Support\n\n- **Hono** - Primary target (serverless-optimized)\n- **TanStack** - Planned\n- **Fastify** - Planned\n- **Astro** - Planned\n- **Express** - Planned\n\n## Testing with Moodle Sandbox\n\nTest your implementation with the public Moodle sandbox\n\n### 1. Access Moodle Sandbox\n\n- URL: https://sandbox.moodledemo.net/\n- Login with administrator credentials (available on the site)\n\n### 2. Configure LTI Tool\n\n1. Go to **Site Administration** → **Plugins** → **Activity modules** → **External tool** → **Manage tools**\n2. Click **\"configure a tool manually\"**\n3. Fill in the configuration\n\n| Field              | Value                                |\n| ------------------ | ------------------------------------ |\n| Tool name          | `lti-tool`                           |\n| Tool URL           | `https://your-domain.com`            |\n| LTI version        | **LTI 1.3**                          |\n| Public key type    | **Keyset URL**                       |\n| Public keyset      | `https://your-domain.com/lti/jwks`   |\n| Login URL          | `https://your-domain.com/lti/login`  |\n| Redirection URI(s) | `https://your-domain.com/lti/launch` |\n\n4. Enable services\n   - **IMS LTI Assignment and Grade Services**: Use this service for grade sync\n   - **IMS LTI Names and Role Provisioning**: Use this service to retrieve members\n   - **Tool Settings**: Use this service\n\n5. Set privacy options (optional)\n   - Share launcher's name: **Always**\n   - Share launcher's email: **Always**\n\n### 3. Get Configuration Details\n\nAfter saving, click the magnifying glass to view tool details and update your code\n\n```typescript\nconst clientId = await ltiTool.addClient({\n  name: 'Moodle Sandbox',\n  clientId: 'YOUR_CLIENT_ID_FROM_MOODLE', // Copy from tool details\n  iss: 'https://sandbox.moodledemo.net',\n  jwksUrl: 'https://sandbox.moodledemo.net/mod/lti/certs.php',\n  authUrl: 'https://sandbox.moodledemo.net/mod/lti/auth.php',\n  tokenUrl: 'https://sandbox.moodledemo.net/mod/lti/token.php',\n});\n\nawait ltiTool.addDeployment(clientId, {\n  deploymentId: 'YOUR_DEPLOYMENT_ID_FROM_MOODLE', // Copy from tool details\n  name: 'Default Deployment',\n});\n```\n\n### 4. Add to Course\n\n1. Edit any course\n2. Add activity → **External tool**\n3. Select your configured tool\n4. Test the launch!\n\n## Security\n\nProduction-ready security features\n\n- **JWT Signature Verification** - Using platform JWKS\n- **Nonce Validation** - Prevents replay attacks\n- **State Verification** - CSRF protection\n- **Client ID Validation** - Ensures proper tool targeting\n- **Deployment Verification** - Validates deployment context\n- **Cookie-Free Design** - Works in all iframe contexts, immune to 3rd party cookie restrictions\n\n\u003e **Production Note**: The quick start example uses `crypto.subtle.generateKey()` for simplicity. In production, use proper key management (AWS Parameter Store SecureString, AWS KMS, HashiCorp Vault, etc.).\n\n## Examples (coming soon)\n\nExamples repository coming soon. Watch this repo for updates!\n\n## Get Involved\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n**Need help?** [Open an issue](https://github.com/lti-tool/lti-tool/issues) or [start a discussion](https://github.com/lti-tool/lti-tool/discussions)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flti-tool%2Flti-tool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flti-tool%2Flti-tool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flti-tool%2Flti-tool/lists"}