@lti-tool/core 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +60 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +3 -0
  6. package/dist/interfaces/index.d.ts +7 -0
  7. package/dist/interfaces/index.d.ts.map +1 -0
  8. package/dist/interfaces/index.js +1 -0
  9. package/dist/interfaces/jwks.d.ts +18 -0
  10. package/dist/interfaces/jwks.d.ts.map +1 -0
  11. package/dist/interfaces/jwks.js +1 -0
  12. package/dist/interfaces/ltiClient.d.ts +24 -0
  13. package/dist/interfaces/ltiClient.d.ts.map +1 -0
  14. package/dist/interfaces/ltiClient.js +1 -0
  15. package/dist/interfaces/ltiConfig.d.ts +26 -0
  16. package/dist/interfaces/ltiConfig.d.ts.map +1 -0
  17. package/dist/interfaces/ltiConfig.js +1 -0
  18. package/dist/interfaces/ltiDeployment.d.ts +15 -0
  19. package/dist/interfaces/ltiDeployment.d.ts.map +1 -0
  20. package/dist/interfaces/ltiDeployment.js +1 -0
  21. package/dist/interfaces/ltiLaunchConfig.d.ts +19 -0
  22. package/dist/interfaces/ltiLaunchConfig.d.ts.map +1 -0
  23. package/dist/interfaces/ltiLaunchConfig.js +1 -0
  24. package/dist/interfaces/ltiSession.d.ts +110 -0
  25. package/dist/interfaces/ltiSession.d.ts.map +1 -0
  26. package/dist/interfaces/ltiSession.js +1 -0
  27. package/dist/interfaces/ltiStorage.d.ts +122 -0
  28. package/dist/interfaces/ltiStorage.d.ts.map +1 -0
  29. package/dist/interfaces/ltiStorage.js +1 -0
  30. package/dist/ltiTool.d.ts +184 -0
  31. package/dist/ltiTool.d.ts.map +1 -0
  32. package/dist/ltiTool.js +305 -0
  33. package/dist/schemas/client.schema.d.ts +33 -0
  34. package/dist/schemas/client.schema.d.ts.map +1 -0
  35. package/dist/schemas/client.schema.js +14 -0
  36. package/dist/schemas/common.schema.d.ts +6 -0
  37. package/dist/schemas/common.schema.d.ts.map +1 -0
  38. package/dist/schemas/common.schema.js +5 -0
  39. package/dist/schemas/deployment.schema.d.ts +8 -0
  40. package/dist/schemas/deployment.schema.d.ts.map +1 -0
  41. package/dist/schemas/deployment.schema.js +11 -0
  42. package/dist/schemas/index.d.ts +5 -0
  43. package/dist/schemas/index.d.ts.map +1 -0
  44. package/dist/schemas/index.js +4 -0
  45. package/dist/schemas/lti13/ags/scoreSubmission.schema.d.ts +34 -0
  46. package/dist/schemas/lti13/ags/scoreSubmission.schema.d.ts.map +1 -0
  47. package/dist/schemas/lti13/ags/scoreSubmission.schema.js +41 -0
  48. package/dist/schemas/lti13/claims/baseJwtClaims.schema.d.ts +11 -0
  49. package/dist/schemas/lti13/claims/baseJwtClaims.schema.d.ts.map +1 -0
  50. package/dist/schemas/lti13/claims/baseJwtClaims.schema.js +10 -0
  51. package/dist/schemas/lti13/claims/contextClaims.schema.d.ts +11 -0
  52. package/dist/schemas/lti13/claims/contextClaims.schema.d.ts.map +1 -0
  53. package/dist/schemas/lti13/claims/contextClaims.schema.js +14 -0
  54. package/dist/schemas/lti13/claims/coreLtiClaims.schema.d.ts +9 -0
  55. package/dist/schemas/lti13/claims/coreLtiClaims.schema.d.ts.map +1 -0
  56. package/dist/schemas/lti13/claims/coreLtiClaims.schema.js +11 -0
  57. package/dist/schemas/lti13/claims/platformClaims.schema.d.ts +19 -0
  58. package/dist/schemas/lti13/claims/platformClaims.schema.d.ts.map +1 -0
  59. package/dist/schemas/lti13/claims/platformClaims.schema.js +24 -0
  60. package/dist/schemas/lti13/claims/privacyClaims.schema.d.ts +8 -0
  61. package/dist/schemas/lti13/claims/privacyClaims.schema.d.ts.map +1 -0
  62. package/dist/schemas/lti13/claims/privacyClaims.schema.js +7 -0
  63. package/dist/schemas/lti13/claims/serviceClaims.schema.d.ts +20 -0
  64. package/dist/schemas/lti13/claims/serviceClaims.schema.d.ts.map +1 -0
  65. package/dist/schemas/lti13/claims/serviceClaims.schema.js +25 -0
  66. package/dist/schemas/lti13/lti13JwtPayload.schema.d.ts +66 -0
  67. package/dist/schemas/lti13/lti13JwtPayload.schema.d.ts.map +1 -0
  68. package/dist/schemas/lti13/lti13JwtPayload.schema.js +22 -0
  69. package/dist/schemas/lti13/lti13Launch.schema.d.ts +14 -0
  70. package/dist/schemas/lti13/lti13Launch.schema.d.ts.map +1 -0
  71. package/dist/schemas/lti13/lti13Launch.schema.js +13 -0
  72. package/dist/schemas/lti13/lti13Login.schema.d.ts +23 -0
  73. package/dist/schemas/lti13/lti13Login.schema.d.ts.map +1 -0
  74. package/dist/schemas/lti13/lti13Login.schema.js +16 -0
  75. package/dist/services/ags.service.d.ts +38 -0
  76. package/dist/services/ags.service.d.ts.map +1 -0
  77. package/dist/services/ags.service.js +69 -0
  78. package/dist/services/session.service.d.ts +11 -0
  79. package/dist/services/session.service.d.ts.map +1 -0
  80. package/dist/services/session.service.js +103 -0
  81. package/dist/services/token.service.d.ts +36 -0
  82. package/dist/services/token.service.d.ts.map +1 -0
  83. package/dist/services/token.service.js +74 -0
  84. package/dist/utils/launchConfigValidation.d.ts +3 -0
  85. package/dist/utils/launchConfigValidation.d.ts.map +1 -0
  86. package/dist/utils/launchConfigValidation.js +7 -0
  87. package/package.json +53 -0
  88. package/src/index.ts +3 -0
  89. package/src/interfaces/index.ts +6 -0
  90. package/src/interfaces/jwks.ts +20 -0
  91. package/src/interfaces/ltiClient.ts +24 -0
  92. package/src/interfaces/ltiConfig.ts +31 -0
  93. package/src/interfaces/ltiDeployment.ts +17 -0
  94. package/src/interfaces/ltiLaunchConfig.ts +23 -0
  95. package/src/interfaces/ltiSession.ts +119 -0
  96. package/src/interfaces/ltiStorage.ts +161 -0
  97. package/src/ltiTool.ts +394 -0
  98. package/src/schemas/client.schema.ts +17 -0
  99. package/src/schemas/common.schema.ts +7 -0
  100. package/src/schemas/deployment.schema.ts +12 -0
  101. package/src/schemas/index.ts +10 -0
  102. package/src/schemas/lti13/ags/scoreSubmission.schema.ts +54 -0
  103. package/src/schemas/lti13/claims/baseJwtClaims.schema.ts +11 -0
  104. package/src/schemas/lti13/claims/contextClaims.schema.ts +16 -0
  105. package/src/schemas/lti13/claims/coreLtiClaims.schema.ts +12 -0
  106. package/src/schemas/lti13/claims/platformClaims.schema.ts +27 -0
  107. package/src/schemas/lti13/claims/privacyClaims.schema.ts +8 -0
  108. package/src/schemas/lti13/claims/serviceClaims.schema.ts +28 -0
  109. package/src/schemas/lti13/lti13JwtPayload.schema.ts +36 -0
  110. package/src/schemas/lti13/lti13Launch.schema.ts +15 -0
  111. package/src/schemas/lti13/lti13Login.schema.ts +18 -0
  112. package/src/services/ags.service.ts +92 -0
  113. package/src/services/session.service.ts +115 -0
  114. package/src/services/token.service.ts +84 -0
  115. package/src/utils/launchConfigValidation.ts +16 -0
  116. package/tsconfig.json +8 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Creates an LTI session object from a validated LTI 1.3 JWT payload.
3
+ * Extracts user information, context data, and available services into a structured session.
4
+ *
5
+ * @param lti13JwtPayload - Validated LTI 1.3 JWT payload from successful launch
6
+ * @returns Complete LTI session object with user, context, and service information
7
+ */
8
+ // oxlint-disable-next-line max-lines-per-function
9
+ export function createSession(lti13JwtPayload) {
10
+ const roles = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/roles'] || [];
11
+ const context = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/context'];
12
+ const platform = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/tool_platform'];
13
+ const resourceLink = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/resource_link'];
14
+ const customClaims = lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/custom'] || {};
15
+ const agsEndpoint = lti13JwtPayload['https://purl.imsglobal.org/spec/lti-ags/claim/endpoint'];
16
+ const nrpsService = lti13JwtPayload['https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice'];
17
+ const deepLinkingSettings = lti13JwtPayload['https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings'];
18
+ const isInstructor = roles.some((role) => role.includes('Instructor'));
19
+ const isStudent = roles.some((role) => role.includes('Learner'));
20
+ const isAdmin = roles.some((role) => role.includes('Administrator'));
21
+ const services = {};
22
+ if (agsEndpoint) {
23
+ services.ags = {
24
+ lineitem: agsEndpoint.lineitem,
25
+ lineitems: agsEndpoint.lineitems,
26
+ scopes: agsEndpoint.scope || [],
27
+ };
28
+ }
29
+ if (nrpsService) {
30
+ services.nrps = {
31
+ membershipUrl: nrpsService.context_memberships_url,
32
+ versions: nrpsService.service_versions || [],
33
+ };
34
+ }
35
+ if (deepLinkingSettings) {
36
+ services.deepLinking = {
37
+ returnUrl: deepLinkingSettings.deep_link_return_url,
38
+ acceptTypes: deepLinkingSettings.accept_types || [],
39
+ acceptPresentationDocumentTargets: deepLinkingSettings.accept_presentation_document_targets || [],
40
+ acceptMediaTypes: deepLinkingSettings.accept_media_types,
41
+ autoCreate: deepLinkingSettings.auto_create,
42
+ data: deepLinkingSettings.data,
43
+ };
44
+ }
45
+ // Extract simplified roles
46
+ const simplifiedRoles = [];
47
+ for (const role of roles) {
48
+ if (role.includes('Instructor'))
49
+ simplifiedRoles.push('instructor');
50
+ if (role.includes('Learner'))
51
+ simplifiedRoles.push('student');
52
+ if (role.includes('Administrator'))
53
+ simplifiedRoles.push('admin');
54
+ if (role.includes('ContentDeveloper'))
55
+ simplifiedRoles.push('content-developer');
56
+ if (role.includes('Member'))
57
+ simplifiedRoles.push('member');
58
+ }
59
+ // Remove duplicates
60
+ const uniqueRoles = [...new Set(simplifiedRoles)];
61
+ return {
62
+ jwtPayload: lti13JwtPayload,
63
+ id: crypto.randomUUID(),
64
+ user: {
65
+ id: lti13JwtPayload.sub,
66
+ name: lti13JwtPayload.name,
67
+ email: lti13JwtPayload.email,
68
+ familyName: lti13JwtPayload.family_name,
69
+ givenName: lti13JwtPayload.given_name,
70
+ roles: uniqueRoles,
71
+ },
72
+ context: {
73
+ id: context?.id || '',
74
+ label: context?.label || context?.id || '',
75
+ title: context?.title || '',
76
+ },
77
+ platform: {
78
+ issuer: lti13JwtPayload.iss,
79
+ clientId: Array.isArray(lti13JwtPayload.aud)
80
+ ? lti13JwtPayload.aud[0]
81
+ : lti13JwtPayload.aud,
82
+ deploymentId: lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/deployment_id'],
83
+ name: platform?.name || lti13JwtPayload.iss,
84
+ },
85
+ launch: {
86
+ target: lti13JwtPayload['https://purl.imsglobal.org/spec/lti/claim/target_link_uri'],
87
+ },
88
+ resourceLink: resourceLink
89
+ ? {
90
+ id: resourceLink.id,
91
+ title: resourceLink.title,
92
+ }
93
+ : undefined,
94
+ customParameters: customClaims,
95
+ services: Object.keys(services).length > 0 ? services : undefined,
96
+ isAdmin,
97
+ isInstructor,
98
+ isStudent,
99
+ isAssignmentAndGradesAvailable: !!agsEndpoint,
100
+ isDeepLinkingAvailable: !!deepLinkingSettings,
101
+ isNameAndRolesAvailable: !!nrpsService,
102
+ };
103
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Service for handling OAuth2 client credentials flow and JWT client assertions.
3
+ * Used for obtaining bearer tokens to access LTI Advantage services (AGS, NRPS, etc.).
4
+ *
5
+ * Implements RFC 7523 (JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants)
6
+ * as required by LTI 1.3 security framework.
7
+ */
8
+ export declare class TokenService {
9
+ private keyPair;
10
+ private keyId;
11
+ /**
12
+ * Creates a new TokenService instance.
13
+ *
14
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
15
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
16
+ */
17
+ constructor(keyPair: CryptoKeyPair, keyId?: string);
18
+ /**
19
+ * Creates a JWT client assertion for OAuth2 client credentials flow.
20
+ *
21
+ * @param clientId - OAuth2 client identifier
22
+ * @param tokenUrl - Platform's token endpoint URL
23
+ * @returns Signed JWT client assertion
24
+ */
25
+ createClientAssertion(clientId: string, tokenUrl: string): Promise<string>;
26
+ /**
27
+ * Obtains an OAuth2 bearer token using client credentials flow with JWT assertion.
28
+ *
29
+ * @param clientId - OAuth2 client identifier
30
+ * @param tokenUrl - Platform's token endpoint URL
31
+ * @param scope - Requested OAuth2 scope (e.g., AGS score scope)
32
+ * @returns Bearer access token for API calls
33
+ */
34
+ getBearerToken(clientId: string, tokenUrl: string, scope: string): Promise<string>;
35
+ }
36
+ //# sourceMappingURL=token.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.service.d.ts","sourceRoot":"","sources":["../../src/services/token.service.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,qBAAa,YAAY;IAQrB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,KAAK;IARf;;;;;OAKG;gBAEO,OAAO,EAAE,aAAa,EACtB,KAAK,SAAS;IAGxB;;;;;;OAMG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBhF;;;;;;;OAOG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;CA0BnB"}
@@ -0,0 +1,74 @@
1
+ import { SignJWT } from 'jose';
2
+ /**
3
+ * Service for handling OAuth2 client credentials flow and JWT client assertions.
4
+ * Used for obtaining bearer tokens to access LTI Advantage services (AGS, NRPS, etc.).
5
+ *
6
+ * Implements RFC 7523 (JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants)
7
+ * as required by LTI 1.3 security framework.
8
+ */
9
+ export class TokenService {
10
+ keyPair;
11
+ keyId;
12
+ /**
13
+ * Creates a new TokenService instance.
14
+ *
15
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
16
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
17
+ */
18
+ constructor(keyPair, keyId = 'main') {
19
+ this.keyPair = keyPair;
20
+ this.keyId = keyId;
21
+ }
22
+ /**
23
+ * Creates a JWT client assertion for OAuth2 client credentials flow.
24
+ *
25
+ * @param clientId - OAuth2 client identifier
26
+ * @param tokenUrl - Platform's token endpoint URL
27
+ * @returns Signed JWT client assertion
28
+ */
29
+ async createClientAssertion(clientId, tokenUrl) {
30
+ return await new SignJWT({
31
+ iss: clientId,
32
+ sub: clientId,
33
+ aud: tokenUrl,
34
+ iat: Math.floor(Date.now() / 1000),
35
+ exp: Math.floor(Date.now() / 1000) + 300,
36
+ jti: crypto.randomUUID(),
37
+ })
38
+ .setProtectedHeader({
39
+ alg: 'RS256',
40
+ kid: this.keyId,
41
+ typ: 'JWT',
42
+ })
43
+ .sign(this.keyPair.privateKey);
44
+ }
45
+ /**
46
+ * Obtains an OAuth2 bearer token using client credentials flow with JWT assertion.
47
+ *
48
+ * @param clientId - OAuth2 client identifier
49
+ * @param tokenUrl - Platform's token endpoint URL
50
+ * @param scope - Requested OAuth2 scope (e.g., AGS score scope)
51
+ * @returns Bearer access token for API calls
52
+ */
53
+ async getBearerToken(clientId, tokenUrl, scope) {
54
+ const assertion = await this.createClientAssertion(clientId, tokenUrl);
55
+ const response = await fetch(tokenUrl, {
56
+ method: 'POST',
57
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
58
+ body: new URLSearchParams({
59
+ grant_type: 'client_credentials',
60
+ client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
61
+ client_assertion: assertion,
62
+ scope,
63
+ }),
64
+ });
65
+ if (!response.ok) {
66
+ throw new Error(`Token request failed: ${response.status} ${response.statusText}`);
67
+ }
68
+ const tokenData = await response.json();
69
+ if (!tokenData.access_token) {
70
+ throw new Error('Token response missing access_token');
71
+ }
72
+ return tokenData.access_token;
73
+ }
74
+ }
@@ -0,0 +1,3 @@
1
+ import type { LTILaunchConfig, LTIStorage } from '../interfaces/index.js';
2
+ export declare function getValidLaunchConfig(storage: LTIStorage, iss: string, clientId: string, deploymentId: string): Promise<LTILaunchConfig>;
3
+ //# sourceMappingURL=launchConfigValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launchConfigValidation.d.ts","sourceRoot":"","sources":["../../src/utils/launchConfigValidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1E,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,CAAC,CAQ1B"}
@@ -0,0 +1,7 @@
1
+ export async function getValidLaunchConfig(storage, iss, clientId, deploymentId) {
2
+ const launchConfig = await storage.getLaunchConfig(iss, clientId, deploymentId);
3
+ if (!launchConfig) {
4
+ throw new Error('No valid launch config found');
5
+ }
6
+ return launchConfig;
7
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@lti-tool/core",
3
+ "version": "0.9.0",
4
+ "description": "LTI 1.3 implementation for Node.js",
5
+ "keywords": [
6
+ "lti",
7
+ "lti13",
8
+ "education",
9
+ "edtech",
10
+ "serverless",
11
+ "nodejs"
12
+ ],
13
+ "author": "jamesjoplin",
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/lti-tool/lti-tool.git",
18
+ "directory": "packages/core"
19
+ },
20
+ "homepage": "https://github.com/lti-tool/lti-tool#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/lti-tool/lti-tool/issues"
23
+ },
24
+ "type": "module",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "dev": "tsc --watch",
36
+ "test": "vitest"
37
+ },
38
+ "dependencies": {
39
+ "jose": "^6.1.0",
40
+ "zod": "^4.1.11"
41
+ },
42
+ "peerDependencies": {
43
+ "pino": "^9.11.0"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "pino": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "pino": "^9.12.0"
52
+ }
53
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './interfaces/index.js';
2
+ export * from './schemas/index.js';
3
+ export { LTITool } from './ltiTool.js';
@@ -0,0 +1,6 @@
1
+ export type { LTIClient } from './ltiClient.js';
2
+ export type { LTIConfig } from './ltiConfig.js';
3
+ export type { LTIDeployment } from './ltiDeployment.js';
4
+ export type { LTILaunchConfig } from './ltiLaunchConfig.js';
5
+ export type { LTISession } from './ltiSession.js';
6
+ export type { LTIStorage } from './ltiStorage.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * JSON Web Key Set (JWKS) structure as defined by RFC 7517.
3
+ * Used to expose public keys for JWT signature verification.
4
+ */
5
+ export interface JWKS {
6
+ /** Array of JSON Web Key (JWK) objects */
7
+ keys: Array<{
8
+ /** Key usage - typically 'sig' for signature verification */
9
+ use: string;
10
+
11
+ /** Algorithm intended for use with this key - typically 'RS256' for LTI */
12
+ alg: string;
13
+
14
+ /** Key identifier used to match keys in JWT headers */
15
+ kid: string;
16
+
17
+ /** Additional JWK parameters (kty, n, e, etc.) as defined by RFC 7517 */
18
+ [key: string]: unknown;
19
+ }>;
20
+ }
@@ -0,0 +1,24 @@
1
+ import type { LTIDeployment } from './ltiDeployment';
2
+
3
+ /**
4
+ * Represents an LTI (Learning Tools Interoperability) platform configuration.
5
+ * Contains all necessary endpoints and identifiers for LTI 1.3 integration.
6
+ */
7
+ export interface LTIClient {
8
+ /** Unique identifier for the client */
9
+ id: string;
10
+ /** human-readable name for the platform */
11
+ name: string;
12
+ /** Platform issuer (unique identifier) */
13
+ iss: string;
14
+ /** Your app's client ID on this platform */
15
+ clientId: string;
16
+ /** Platform's auth endpoint */
17
+ authUrl: string;
18
+ /** Platform's token endpoint */
19
+ tokenUrl: string;
20
+ /** Platform's JWKS endpoint */
21
+ jwksUrl: string;
22
+ /** Array of deployment IDs associated with this platform */
23
+ deployments: LTIDeployment[];
24
+ }
@@ -0,0 +1,31 @@
1
+ import type { Logger } from 'pino';
2
+
3
+ import type { LTIStorage } from './ltiStorage.js';
4
+
5
+ /**
6
+ * Configuration object for initializing an LTI Tool instance.
7
+ * Contains cryptographic keys, secrets, and storage adapter.
8
+ */
9
+ export interface LTIConfig {
10
+ /** Secret key used for signing state JWTs during OIDC flow (minimum 32 bytes recommended) */
11
+ stateSecret: Uint8Array;
12
+
13
+ /** RSA key pair for signing JWTs and providing JWKS endpoint */
14
+ keyPair: CryptoKeyPair;
15
+
16
+ /** Storage adapter for persisting platforms, sessions, and nonces */
17
+ storage: LTIStorage;
18
+
19
+ /** Optional pino logger */
20
+ logger?: Logger;
21
+
22
+ /** Security configuration options */
23
+ security?: {
24
+ /** Key ID for JWKS and JWT signing (defaults to 'main') */
25
+ keyId?: string;
26
+ /** State JWT expiration time in seconds (defaults to 600 = 10 minutes) */
27
+ stateExpirationSeconds?: number;
28
+ /** Nonce expiration time in seconds (defaults to 600 = 10 minutes) */
29
+ nonceExpirationSeconds?: number;
30
+ };
31
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Represents a specific deployment of your LTI tool within a platform/LMS.
3
+ * Each platform can have multiple deployments (e.g., different courses, contexts).
4
+ */
5
+ export interface LTIDeployment {
6
+ /** Internal stable UUID for this deployment configuration */
7
+ id: string;
8
+
9
+ /** LMS-provided deployment identifier used in LTI launch requests */
10
+ deploymentId: string;
11
+
12
+ /** Optional human-readable name for this deployment */
13
+ name?: string;
14
+
15
+ /** Optional description of what this deployment is used for */
16
+ description?: string;
17
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Configuration required for LTI 1.3 launch authentication flow.
3
+ * Contains platform endpoints and identifiers needed for OIDC authentication.
4
+ */
5
+ export interface LTILaunchConfig {
6
+ /** Platform issuer URL that uniquely identifies the LMS */
7
+ iss: string;
8
+
9
+ /** OAuth2 client identifier assigned to your tool by the platform */
10
+ clientId: string;
11
+
12
+ /** Deployment identifier within the platform context */
13
+ deploymentId: string;
14
+
15
+ /** Platform's OIDC authentication endpoint URL */
16
+ authUrl: string;
17
+
18
+ /** Platform's OAuth2 token endpoint URL for service access */
19
+ tokenUrl: string;
20
+
21
+ /** Platform's JSON Web Key Set endpoint URL for JWT verification */
22
+ jwksUrl: string;
23
+ }
@@ -0,0 +1,119 @@
1
+ import type { JWTPayload } from 'jose';
2
+
3
+ /**
4
+ * Represents an active LTI session containing user information, context data,
5
+ * and available services after successful launch verification.
6
+ */
7
+ export interface LTISession {
8
+ /** Original JWT payload from the platform for reference */
9
+ jwtPayload: JWTPayload;
10
+
11
+ /** Unique session identifier (UUID) */
12
+ id: string;
13
+
14
+ /** User information extracted from LTI claims */
15
+ user: {
16
+ /** Unique user identifier from the platform */
17
+ id: string;
18
+ /** User's display name */
19
+ name?: string;
20
+ /** User's email address */
21
+ email?: string;
22
+ /** User's family/last name */
23
+ familyName?: string;
24
+ /** User's given/first name */
25
+ givenName?: string;
26
+ /** Array of LTI role URIs (e.g., 'http://purl.imsglobal.org/vocab/lis/v2/membership#Instructor') */
27
+ roles: string[];
28
+ };
29
+
30
+ /** Course/context information */
31
+ context: {
32
+ /** Unique context identifier from the platform */
33
+ id: string;
34
+ /** Short context label (e.g., course code) */
35
+ label: string;
36
+ /** Full context title (e.g., course name) */
37
+ title: string;
38
+ };
39
+
40
+ /** Platform identification */
41
+ platform: {
42
+ /** Platform issuer URL */
43
+ issuer: string;
44
+ /** OAuth2 client identifier */
45
+ clientId: string;
46
+ /** Deployment identifier */
47
+ deploymentId: string;
48
+ /** Human-readable platform name */
49
+ name: string;
50
+ };
51
+
52
+ /** Launch target information */
53
+ launch: {
54
+ /** Target link URI where user should be directed */
55
+ target: string;
56
+ };
57
+
58
+ /** Resource link information (if applicable) */
59
+ resourceLink?: {
60
+ /** Unique resource link identifier */
61
+ id: string;
62
+ /** Resource link title */
63
+ title?: string;
64
+ };
65
+
66
+ /** Available LTI Advantage services */
67
+ services?: {
68
+ /** Assignment and Grade Services (AGS) configuration */
69
+ ags?: {
70
+ /** Single line item endpoint URL */
71
+ lineitem?: string;
72
+ /** Line items collection endpoint URL */
73
+ lineitems?: string;
74
+ /** Available AGS scopes */
75
+ scopes: string[];
76
+ };
77
+ /** Names and Role Provisioning Services (NRPS) configuration */
78
+ nrps?: {
79
+ /** Membership endpoint URL */
80
+ membershipUrl: string;
81
+ /** Supported NRPS versions */
82
+ versions: string[];
83
+ };
84
+ /** Deep Linking configuration */
85
+ deepLinking?: {
86
+ /** URL to return deep linking response */
87
+ returnUrl: string;
88
+ /** Accepted content types */
89
+ acceptTypes: string[];
90
+ /** Accepted presentation targets */
91
+ acceptPresentationDocumentTargets: string[];
92
+ /** Accepted media types */
93
+ acceptMediaTypes?: string;
94
+ /** Whether multiple items can be selected */
95
+ acceptMultiple: boolean;
96
+ /** Whether items should be auto-created */
97
+ autoCreate: boolean;
98
+ /** Platform-specific data to return */
99
+ data?: string;
100
+ };
101
+ };
102
+
103
+ /** Custom parameters passed from platform */
104
+ customParameters: Record<string, string>;
105
+
106
+ /** Convenience flags for role checking */
107
+ /** True if user has administrator privileges */
108
+ isAdmin: boolean;
109
+ /** True if user has instructor/teacher role */
110
+ isInstructor: boolean;
111
+ /** True if user has student/learner role */
112
+ isStudent: boolean;
113
+ /** True if Assignment and Grade Services are available */
114
+ isAssignmentAndGradesAvailable: boolean;
115
+ /** True if Deep Linking is available */
116
+ isDeepLinkingAvailable: boolean;
117
+ /** True if Names and Role Provisioning Services are available */
118
+ isNameAndRolesAvailable: boolean;
119
+ }