@lti-tool/core 0.12.2 → 0.13.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @lti-tool/core
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d5a2793: Pass through keyId to utilize in kid when signing the Deep Linking JWT
8
+
3
9
  ## 0.12.2
4
10
 
5
11
  ### Patch Changes
package/dist/ltiTool.d.ts CHANGED
@@ -8,6 +8,7 @@ import { type DynamicRegistrationForm, type LTI13JwtPayload, type RegistrationRe
8
8
  import { type CreateLineItem, type LineItem, type LineItems, type UpdateLineItem } from './schemas/lti13/ags/lineItem.schema.js';
9
9
  import { type Results } from './schemas/lti13/ags/result.schema.js';
10
10
  import { type ScoreSubmission } from './schemas/lti13/ags/scoreSubmission.schema.js';
11
+ import { type DeepLinkingContentItem } from './schemas/lti13/deepLinking/contentItem.schema.js';
11
12
  import { type OpenIDConfiguration } from './schemas/lti13/dynamicRegistration/openIDConfiguration.schema.js';
12
13
  import { type Member } from './schemas/lti13/nrps/contextMembership.schema.js';
13
14
  /**
@@ -41,6 +42,7 @@ export declare class LTITool {
41
42
  private tokenService;
42
43
  private agsService;
43
44
  private nrpsService;
45
+ private deepLinkingService;
44
46
  private dynamicRegistrationService?;
45
47
  /**
46
48
  * Creates a new LTI Tool instance.
@@ -197,6 +199,28 @@ export declare class LTITool {
197
199
  * ```
198
200
  */
199
201
  getMembers(session: LTISession): Promise<Member[]>;
202
+ /**
203
+ * Creates a Deep Linking response with selected content items.
204
+ * Generates a signed JWT and returns HTML form that auto-submits to the platform.
205
+ *
206
+ * @param session - Active LTI session containing Deep Linking configuration
207
+ * @param contentItems - Array of content items selected by the user
208
+ * @returns HTML string containing auto-submit form
209
+ * @throws {Error} When Deep Linking is not available for the session
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * const html = await ltiTool.createDeepLinkingResponse(session, [
214
+ * {
215
+ * type: 'ltiResourceLink',
216
+ * title: 'Quiz 1',
217
+ * url: 'https://tool.example.com/quiz/1'
218
+ * }
219
+ * ]);
220
+ * // Render the HTML to return content items to platform
221
+ * ```
222
+ */
223
+ createDeepLinkingResponse(session: LTISession, contentItems: DeepLinkingContentItem[]): Promise<string>;
200
224
  /**
201
225
  * Fetches and validates the OpenID Connect configuration from an LTI platform during dynamic registration.
202
226
  * Validates that the OIDC endpoint and issuer have matching hostnames for security.
@@ -1 +1 @@
1
- {"version":3,"file":"ltiTool.d.ts","sourceRoot":"","sources":["../src/ltiTool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ltiTool.d.ts","sourceRoot":"","sources":["../src/ltiTool.ts"],"names":[],"mappings":"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"}
package/dist/ltiTool.js CHANGED
@@ -5,6 +5,7 @@ import { LineItemSchema, LineItemsSchema, } from './schemas/lti13/ags/lineItem.s
5
5
  import { ResultsSchema } from './schemas/lti13/ags/result.schema.js';
6
6
  import { NRPSContextMembershipResponseSchema, } from './schemas/lti13/nrps/contextMembership.schema.js';
7
7
  import { AGSService } from './services/ags.service.js';
8
+ import { DeepLinkingService } from './services/deepLinking.service.js';
8
9
  import { DynamicRegistrationService } from './services/dynamicRegistration.service.js';
9
10
  import { NRPSService } from './services/nrps.service.js';
10
11
  import { createSession } from './services/session.service.js';
@@ -41,6 +42,7 @@ export class LTITool {
41
42
  tokenService;
42
43
  agsService;
43
44
  nrpsService;
45
+ deepLinkingService;
44
46
  dynamicRegistrationService;
45
47
  /**
46
48
  * Creates a new LTI Tool instance.
@@ -60,6 +62,7 @@ export class LTITool {
60
62
  this.tokenService = new TokenService(this.config.keyPair, this.config.security?.keyId ?? 'main');
61
63
  this.agsService = new AGSService(this.tokenService, this.config.storage, this.logger);
62
64
  this.nrpsService = new NRPSService(this.tokenService, this.config.storage, this.logger);
65
+ this.deepLinkingService = new DeepLinkingService(this.config.keyPair, this.logger, this.config.security?.keyId ?? 'main');
63
66
  if (this.config.dynamicRegistration) {
64
67
  this.dynamicRegistrationService = new DynamicRegistrationService(this.config.storage, this.config.dynamicRegistration, this.logger);
65
68
  }
@@ -365,6 +368,36 @@ export class LTITool {
365
368
  roles: member.roles,
366
369
  }));
367
370
  }
371
+ /**
372
+ * Creates a Deep Linking response with selected content items.
373
+ * Generates a signed JWT and returns HTML form that auto-submits to the platform.
374
+ *
375
+ * @param session - Active LTI session containing Deep Linking configuration
376
+ * @param contentItems - Array of content items selected by the user
377
+ * @returns HTML string containing auto-submit form
378
+ * @throws {Error} When Deep Linking is not available for the session
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const html = await ltiTool.createDeepLinkingResponse(session, [
383
+ * {
384
+ * type: 'ltiResourceLink',
385
+ * title: 'Quiz 1',
386
+ * url: 'https://tool.example.com/quiz/1'
387
+ * }
388
+ * ]);
389
+ * // Render the HTML to return content items to platform
390
+ * ```
391
+ */
392
+ async createDeepLinkingResponse(session, contentItems) {
393
+ if (!session) {
394
+ throw new Error('session is required');
395
+ }
396
+ if (!contentItems) {
397
+ throw new Error('contentItems is required');
398
+ }
399
+ return await this.deepLinkingService.createResponse(session, contentItems);
400
+ }
368
401
  /**
369
402
  * Fetches and validates the OpenID Connect configuration from an LTI platform during dynamic registration.
370
403
  * Validates that the OIDC endpoint and issuer have matching hostnames for security.
@@ -1,4 +1,5 @@
1
1
  export { SessionIdSchema } from './common.schema.js';
2
+ export { ContentItemSchema, type DeepLinkingContentItem, type DeepLinkingFile, type DeepLinkingHtml, type DeepLinkingImage, type DeepLinkingLink, type DeepLinkingLtiResourceLink, } from './lti13/deepLinking/contentItem.schema.js';
2
3
  export { DynamicRegistrationFormSchema, type DynamicRegistrationForm, } from './lti13/dynamicRegistration/ltiDynamicRegistration.schema.js';
3
4
  export { LTIMessagesArraySchema, type LTIMessage, } from './lti13/dynamicRegistration/ltiMessages.schema.js';
4
5
  export { type OpenIDConfiguration } from './lti13/dynamicRegistration/openIDConfiguration.schema.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,6BAA6B,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,8DAA8D,CAAC;AACtE,OAAO,EACL,sBAAsB,EACtB,KAAK,UAAU,GAChB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,2DAA2D,CAAC;AACrG,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,2DAA2D,CAAC;AACnE,OAAO,EACL,0BAA0B,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,4DAA4D,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,0BAA0B,GAChC,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,6BAA6B,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,8DAA8D,CAAC;AACtE,OAAO,EACL,sBAAsB,EACtB,KAAK,UAAU,GAChB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,2DAA2D,CAAC;AACrG,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,2DAA2D,CAAC;AACnE,OAAO,EACL,0BAA0B,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,4DAA4D,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { SessionIdSchema } from './common.schema.js';
2
+ export { ContentItemSchema, } from './lti13/deepLinking/contentItem.schema.js';
2
3
  export { DynamicRegistrationFormSchema, } from './lti13/dynamicRegistration/ltiDynamicRegistration.schema.js';
3
4
  export { LTIMessagesArraySchema, } from './lti13/dynamicRegistration/ltiMessages.schema.js';
4
5
  export { RegistrationRequestSchema, } from './lti13/dynamicRegistration/registrationRequest.schema.js';
@@ -22,13 +22,13 @@ export declare const LineItemSchema: z.ZodObject<{
22
22
  * @see https://www.imsglobal.org/spec/lti-ags/v2p0/#line-item-service
23
23
  */
24
24
  export declare const CreateLineItemSchema: z.ZodObject<{
25
- label: z.ZodString;
26
25
  scoreMaximum: z.ZodNumber;
26
+ label: z.ZodString;
27
27
  resourceId: z.ZodOptional<z.ZodString>;
28
- resourceLinkId: z.ZodOptional<z.ZodString>;
29
28
  tag: z.ZodOptional<z.ZodString>;
30
29
  startDateTime: z.ZodOptional<z.ZodISODateTime>;
31
30
  endDateTime: z.ZodOptional<z.ZodISODateTime>;
31
+ resourceLinkId: z.ZodOptional<z.ZodString>;
32
32
  }, z.core.$strip>;
33
33
  /**
34
34
  * Schema for updating an existing LTI Assignment and Grade Services (AGS) Line Item.
@@ -38,8 +38,8 @@ export declare const CreateLineItemSchema: z.ZodObject<{
38
38
  * @see https://www.imsglobal.org/spec/lti-ags/v2p0/#line-item-service
39
39
  */
40
40
  export declare const UpdateLineItemSchema: z.ZodObject<{
41
- label: z.ZodString;
42
41
  scoreMaximum: z.ZodNumber;
42
+ label: z.ZodString;
43
43
  resourceId: z.ZodOptional<z.ZodString>;
44
44
  tag: z.ZodOptional<z.ZodString>;
45
45
  startDateTime: z.ZodOptional<z.ZodISODateTime>;
@@ -0,0 +1,313 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Zod schema for LTI Resource Link content item.
4
+ * Represents a launchable LTI tool resource that can be embedded in the platform.
5
+ *
6
+ * @property type - Always 'ltiResourceLink' for this content type
7
+ * @property url - Optional launch URL for the resource
8
+ * @property custom - Optional custom parameters passed to the tool on launch
9
+ * @property lineItem - Optional gradebook column configuration for this resource
10
+ * @property available - Optional availability window with start/end dates
11
+ * @property submission - Optional submission window with start/end dates
12
+ *
13
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#lti-resource-link
14
+ */
15
+ export declare const LtiResourceLinkSchema: z.ZodObject<{
16
+ title: z.ZodOptional<z.ZodString>;
17
+ text: z.ZodOptional<z.ZodString>;
18
+ icon: z.ZodOptional<z.ZodObject<{
19
+ url: z.ZodURL;
20
+ width: z.ZodOptional<z.ZodNumber>;
21
+ height: z.ZodOptional<z.ZodNumber>;
22
+ }, z.core.$strip>>;
23
+ thumbnail: z.ZodOptional<z.ZodObject<{
24
+ url: z.ZodURL;
25
+ width: z.ZodOptional<z.ZodNumber>;
26
+ height: z.ZodOptional<z.ZodNumber>;
27
+ }, z.core.$strip>>;
28
+ type: z.ZodLiteral<"ltiResourceLink">;
29
+ url: z.ZodOptional<z.ZodURL>;
30
+ custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
31
+ lineItem: z.ZodOptional<z.ZodObject<{
32
+ scoreMaximum: z.ZodNumber;
33
+ label: z.ZodString;
34
+ resourceId: z.ZodOptional<z.ZodString>;
35
+ tag: z.ZodOptional<z.ZodString>;
36
+ }, z.core.$strip>>;
37
+ available: z.ZodOptional<z.ZodObject<{
38
+ startDateTime: z.ZodOptional<z.ZodISODateTime>;
39
+ endDateTime: z.ZodOptional<z.ZodISODateTime>;
40
+ }, z.core.$strip>>;
41
+ submission: z.ZodOptional<z.ZodObject<{
42
+ startDateTime: z.ZodOptional<z.ZodISODateTime>;
43
+ endDateTime: z.ZodOptional<z.ZodISODateTime>;
44
+ }, z.core.$strip>>;
45
+ }, z.core.$strip>;
46
+ /**
47
+ * Zod schema for simple link content item.
48
+ * Represents a standard web link that can be opened in various ways.
49
+ *
50
+ * @property type - Always 'link' for this content type
51
+ * @property url - Target URL for the link
52
+ * @property embed - Optional HTML embed code
53
+ * @property window - Optional window configuration for opening the link
54
+ * @property iframe - Optional iframe configuration for embedding the link
55
+ *
56
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#link
57
+ */
58
+ export declare const LinkSchema: z.ZodObject<{
59
+ title: z.ZodOptional<z.ZodString>;
60
+ text: z.ZodOptional<z.ZodString>;
61
+ icon: z.ZodOptional<z.ZodObject<{
62
+ url: z.ZodURL;
63
+ width: z.ZodOptional<z.ZodNumber>;
64
+ height: z.ZodOptional<z.ZodNumber>;
65
+ }, z.core.$strip>>;
66
+ thumbnail: z.ZodOptional<z.ZodObject<{
67
+ url: z.ZodURL;
68
+ width: z.ZodOptional<z.ZodNumber>;
69
+ height: z.ZodOptional<z.ZodNumber>;
70
+ }, z.core.$strip>>;
71
+ type: z.ZodLiteral<"link">;
72
+ url: z.ZodURL;
73
+ embed: z.ZodOptional<z.ZodObject<{
74
+ html: z.ZodString;
75
+ }, z.core.$strip>>;
76
+ window: z.ZodOptional<z.ZodObject<{
77
+ targetName: z.ZodOptional<z.ZodString>;
78
+ windowFeatures: z.ZodOptional<z.ZodString>;
79
+ }, z.core.$strip>>;
80
+ iframe: z.ZodOptional<z.ZodObject<{
81
+ width: z.ZodOptional<z.ZodNumber>;
82
+ height: z.ZodOptional<z.ZodNumber>;
83
+ src: z.ZodURL;
84
+ }, z.core.$strip>>;
85
+ }, z.core.$strip>;
86
+ /**
87
+ * Zod schema for HTML fragment content item.
88
+ * Represents raw HTML content to be embedded directly in the platform.
89
+ *
90
+ * @property type - Always 'html' for this content type
91
+ * @property html - HTML content to be embedded
92
+ *
93
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#html-fragment
94
+ */
95
+ export declare const HtmlSchema: z.ZodObject<{
96
+ title: z.ZodOptional<z.ZodString>;
97
+ text: z.ZodOptional<z.ZodString>;
98
+ icon: z.ZodOptional<z.ZodObject<{
99
+ url: z.ZodURL;
100
+ width: z.ZodOptional<z.ZodNumber>;
101
+ height: z.ZodOptional<z.ZodNumber>;
102
+ }, z.core.$strip>>;
103
+ thumbnail: z.ZodOptional<z.ZodObject<{
104
+ url: z.ZodURL;
105
+ width: z.ZodOptional<z.ZodNumber>;
106
+ height: z.ZodOptional<z.ZodNumber>;
107
+ }, z.core.$strip>>;
108
+ type: z.ZodLiteral<"html">;
109
+ html: z.ZodString;
110
+ }, z.core.$strip>;
111
+ /**
112
+ * Zod schema for file content item.
113
+ * Represents a downloadable file resource.
114
+ *
115
+ * @property type - Always 'file' for this content type
116
+ * @property url - URL to download the file
117
+ * @property mediaType - MIME type of the file
118
+ * @property expiresAt - Optional expiration timestamp for the file URL
119
+ *
120
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#file
121
+ */
122
+ export declare const FileSchema: z.ZodObject<{
123
+ title: z.ZodOptional<z.ZodString>;
124
+ text: z.ZodOptional<z.ZodString>;
125
+ icon: z.ZodOptional<z.ZodObject<{
126
+ url: z.ZodURL;
127
+ width: z.ZodOptional<z.ZodNumber>;
128
+ height: z.ZodOptional<z.ZodNumber>;
129
+ }, z.core.$strip>>;
130
+ thumbnail: z.ZodOptional<z.ZodObject<{
131
+ url: z.ZodURL;
132
+ width: z.ZodOptional<z.ZodNumber>;
133
+ height: z.ZodOptional<z.ZodNumber>;
134
+ }, z.core.$strip>>;
135
+ type: z.ZodLiteral<"file">;
136
+ url: z.ZodURL;
137
+ mediaType: z.ZodString;
138
+ expiresAt: z.ZodOptional<z.ZodISODateTime>;
139
+ }, z.core.$strip>;
140
+ /**
141
+ * Zod schema for image content item.
142
+ * Represents an image resource with optional dimensions.
143
+ *
144
+ * @property type - Always 'image' for this content type
145
+ * @property url - URL to the image
146
+ * @property width - Optional image width in pixels
147
+ * @property height - Optional image height in pixels
148
+ *
149
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#image
150
+ */
151
+ export declare const ImageSchema: z.ZodObject<{
152
+ title: z.ZodOptional<z.ZodString>;
153
+ text: z.ZodOptional<z.ZodString>;
154
+ icon: z.ZodOptional<z.ZodObject<{
155
+ url: z.ZodURL;
156
+ width: z.ZodOptional<z.ZodNumber>;
157
+ height: z.ZodOptional<z.ZodNumber>;
158
+ }, z.core.$strip>>;
159
+ thumbnail: z.ZodOptional<z.ZodObject<{
160
+ url: z.ZodURL;
161
+ width: z.ZodOptional<z.ZodNumber>;
162
+ height: z.ZodOptional<z.ZodNumber>;
163
+ }, z.core.$strip>>;
164
+ type: z.ZodLiteral<"image">;
165
+ url: z.ZodURL;
166
+ width: z.ZodOptional<z.ZodNumber>;
167
+ height: z.ZodOptional<z.ZodNumber>;
168
+ }, z.core.$strip>;
169
+ /**
170
+ * Zod schema for validating any Deep Linking content item type.
171
+ * Uses discriminated union on the 'type' field to determine the specific content item schema.
172
+ * Supports: ltiResourceLink, link, html, file, and image content types.
173
+ *
174
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
175
+ */
176
+ export declare const ContentItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
177
+ title: z.ZodOptional<z.ZodString>;
178
+ text: z.ZodOptional<z.ZodString>;
179
+ icon: z.ZodOptional<z.ZodObject<{
180
+ url: z.ZodURL;
181
+ width: z.ZodOptional<z.ZodNumber>;
182
+ height: z.ZodOptional<z.ZodNumber>;
183
+ }, z.core.$strip>>;
184
+ thumbnail: z.ZodOptional<z.ZodObject<{
185
+ url: z.ZodURL;
186
+ width: z.ZodOptional<z.ZodNumber>;
187
+ height: z.ZodOptional<z.ZodNumber>;
188
+ }, z.core.$strip>>;
189
+ type: z.ZodLiteral<"ltiResourceLink">;
190
+ url: z.ZodOptional<z.ZodURL>;
191
+ custom: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
192
+ lineItem: z.ZodOptional<z.ZodObject<{
193
+ scoreMaximum: z.ZodNumber;
194
+ label: z.ZodString;
195
+ resourceId: z.ZodOptional<z.ZodString>;
196
+ tag: z.ZodOptional<z.ZodString>;
197
+ }, z.core.$strip>>;
198
+ available: z.ZodOptional<z.ZodObject<{
199
+ startDateTime: z.ZodOptional<z.ZodISODateTime>;
200
+ endDateTime: z.ZodOptional<z.ZodISODateTime>;
201
+ }, z.core.$strip>>;
202
+ submission: z.ZodOptional<z.ZodObject<{
203
+ startDateTime: z.ZodOptional<z.ZodISODateTime>;
204
+ endDateTime: z.ZodOptional<z.ZodISODateTime>;
205
+ }, z.core.$strip>>;
206
+ }, z.core.$strip>, z.ZodObject<{
207
+ title: z.ZodOptional<z.ZodString>;
208
+ text: z.ZodOptional<z.ZodString>;
209
+ icon: z.ZodOptional<z.ZodObject<{
210
+ url: z.ZodURL;
211
+ width: z.ZodOptional<z.ZodNumber>;
212
+ height: z.ZodOptional<z.ZodNumber>;
213
+ }, z.core.$strip>>;
214
+ thumbnail: z.ZodOptional<z.ZodObject<{
215
+ url: z.ZodURL;
216
+ width: z.ZodOptional<z.ZodNumber>;
217
+ height: z.ZodOptional<z.ZodNumber>;
218
+ }, z.core.$strip>>;
219
+ type: z.ZodLiteral<"link">;
220
+ url: z.ZodURL;
221
+ embed: z.ZodOptional<z.ZodObject<{
222
+ html: z.ZodString;
223
+ }, z.core.$strip>>;
224
+ window: z.ZodOptional<z.ZodObject<{
225
+ targetName: z.ZodOptional<z.ZodString>;
226
+ windowFeatures: z.ZodOptional<z.ZodString>;
227
+ }, z.core.$strip>>;
228
+ iframe: z.ZodOptional<z.ZodObject<{
229
+ width: z.ZodOptional<z.ZodNumber>;
230
+ height: z.ZodOptional<z.ZodNumber>;
231
+ src: z.ZodURL;
232
+ }, z.core.$strip>>;
233
+ }, z.core.$strip>, z.ZodObject<{
234
+ title: z.ZodOptional<z.ZodString>;
235
+ text: z.ZodOptional<z.ZodString>;
236
+ icon: z.ZodOptional<z.ZodObject<{
237
+ url: z.ZodURL;
238
+ width: z.ZodOptional<z.ZodNumber>;
239
+ height: z.ZodOptional<z.ZodNumber>;
240
+ }, z.core.$strip>>;
241
+ thumbnail: z.ZodOptional<z.ZodObject<{
242
+ url: z.ZodURL;
243
+ width: z.ZodOptional<z.ZodNumber>;
244
+ height: z.ZodOptional<z.ZodNumber>;
245
+ }, z.core.$strip>>;
246
+ type: z.ZodLiteral<"html">;
247
+ html: z.ZodString;
248
+ }, z.core.$strip>, z.ZodObject<{
249
+ title: z.ZodOptional<z.ZodString>;
250
+ text: z.ZodOptional<z.ZodString>;
251
+ icon: z.ZodOptional<z.ZodObject<{
252
+ url: z.ZodURL;
253
+ width: z.ZodOptional<z.ZodNumber>;
254
+ height: z.ZodOptional<z.ZodNumber>;
255
+ }, z.core.$strip>>;
256
+ thumbnail: z.ZodOptional<z.ZodObject<{
257
+ url: z.ZodURL;
258
+ width: z.ZodOptional<z.ZodNumber>;
259
+ height: z.ZodOptional<z.ZodNumber>;
260
+ }, z.core.$strip>>;
261
+ type: z.ZodLiteral<"file">;
262
+ url: z.ZodURL;
263
+ mediaType: z.ZodString;
264
+ expiresAt: z.ZodOptional<z.ZodISODateTime>;
265
+ }, z.core.$strip>, z.ZodObject<{
266
+ title: z.ZodOptional<z.ZodString>;
267
+ text: z.ZodOptional<z.ZodString>;
268
+ icon: z.ZodOptional<z.ZodObject<{
269
+ url: z.ZodURL;
270
+ width: z.ZodOptional<z.ZodNumber>;
271
+ height: z.ZodOptional<z.ZodNumber>;
272
+ }, z.core.$strip>>;
273
+ thumbnail: z.ZodOptional<z.ZodObject<{
274
+ url: z.ZodURL;
275
+ width: z.ZodOptional<z.ZodNumber>;
276
+ height: z.ZodOptional<z.ZodNumber>;
277
+ }, z.core.$strip>>;
278
+ type: z.ZodLiteral<"image">;
279
+ url: z.ZodURL;
280
+ width: z.ZodOptional<z.ZodNumber>;
281
+ height: z.ZodOptional<z.ZodNumber>;
282
+ }, z.core.$strip>], "type">;
283
+ /**
284
+ * Type representing a validated LTI Resource Link content item.
285
+ * Used for creating launchable LTI tool resources.
286
+ */
287
+ export type DeepLinkingLtiResourceLink = z.infer<typeof LtiResourceLinkSchema>;
288
+ /**
289
+ * Type representing a validated simple link content item.
290
+ * Used for creating standard web links.
291
+ */
292
+ export type DeepLinkingLink = z.infer<typeof LinkSchema>;
293
+ /**
294
+ * Type representing a validated HTML fragment content item.
295
+ * Used for embedding raw HTML content.
296
+ */
297
+ export type DeepLinkingHtml = z.infer<typeof HtmlSchema>;
298
+ /**
299
+ * Type representing a validated file content item.
300
+ * Used for linking to downloadable files.
301
+ */
302
+ export type DeepLinkingFile = z.infer<typeof FileSchema>;
303
+ /**
304
+ * Type representing a validated image content item.
305
+ * Used for embedding images.
306
+ */
307
+ export type DeepLinkingImage = z.infer<typeof ImageSchema>;
308
+ /**
309
+ * Type representing any validated Deep Linking content item.
310
+ * Can be any of: DeepLinkingLtiResourceLink, DeepLinkingLink, DeepLinkingHtml, DeepLinkingFile, or DeepLinkingImage.
311
+ */
312
+ export type DeepLinkingContentItem = z.infer<typeof ContentItemSchema>;
313
+ //# sourceMappingURL=contentItem.schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentItem.schema.d.ts","sourceRoot":"","sources":["../../../../src/schemas/lti13/deepLinking/contentItem.schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAgCzB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwBhC,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqBrB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;iBAGrB,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;iBAKrB,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;iBAKtB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAM5B,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,163 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Zod schema for base content item properties shared across all content item types.
4
+ * Contains common metadata fields like title, text, icon, and thumbnail.
5
+ *
6
+ * @property title - Optional human-readable title for the content item
7
+ * @property text - Optional descriptive text for the content item
8
+ * @property icon - Optional icon image with URL and dimensions
9
+ * @property thumbnail - Optional thumbnail image with URL and dimensions
10
+ *
11
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
12
+ */
13
+ const BaseContentItemSchema = z.object({
14
+ title: z.string().optional(),
15
+ text: z.string().optional(),
16
+ icon: z
17
+ .object({
18
+ url: z.url(),
19
+ width: z.number().optional(),
20
+ height: z.number().optional(),
21
+ })
22
+ .optional(),
23
+ thumbnail: z
24
+ .object({
25
+ url: z.url(),
26
+ width: z.number().optional(),
27
+ height: z.number().optional(),
28
+ })
29
+ .optional(),
30
+ });
31
+ /**
32
+ * Zod schema for LTI Resource Link content item.
33
+ * Represents a launchable LTI tool resource that can be embedded in the platform.
34
+ *
35
+ * @property type - Always 'ltiResourceLink' for this content type
36
+ * @property url - Optional launch URL for the resource
37
+ * @property custom - Optional custom parameters passed to the tool on launch
38
+ * @property lineItem - Optional gradebook column configuration for this resource
39
+ * @property available - Optional availability window with start/end dates
40
+ * @property submission - Optional submission window with start/end dates
41
+ *
42
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#lti-resource-link
43
+ */
44
+ export const LtiResourceLinkSchema = BaseContentItemSchema.extend({
45
+ type: z.literal('ltiResourceLink'),
46
+ url: z.url().optional(),
47
+ custom: z.record(z.string(), z.string()).optional(),
48
+ lineItem: z
49
+ .object({
50
+ scoreMaximum: z.number().min(0),
51
+ label: z.string(),
52
+ resourceId: z.string().optional(),
53
+ tag: z.string().optional(),
54
+ })
55
+ .optional(),
56
+ available: z
57
+ .object({
58
+ startDateTime: z.iso.datetime().optional(),
59
+ endDateTime: z.iso.datetime().optional(),
60
+ })
61
+ .optional(),
62
+ submission: z
63
+ .object({
64
+ startDateTime: z.iso.datetime().optional(),
65
+ endDateTime: z.iso.datetime().optional(),
66
+ })
67
+ .optional(),
68
+ });
69
+ /**
70
+ * Zod schema for simple link content item.
71
+ * Represents a standard web link that can be opened in various ways.
72
+ *
73
+ * @property type - Always 'link' for this content type
74
+ * @property url - Target URL for the link
75
+ * @property embed - Optional HTML embed code
76
+ * @property window - Optional window configuration for opening the link
77
+ * @property iframe - Optional iframe configuration for embedding the link
78
+ *
79
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#link
80
+ */
81
+ export const LinkSchema = BaseContentItemSchema.extend({
82
+ type: z.literal('link'),
83
+ url: z.url(),
84
+ embed: z
85
+ .object({
86
+ html: z.string(),
87
+ })
88
+ .optional(),
89
+ window: z
90
+ .object({
91
+ targetName: z.string().optional(),
92
+ windowFeatures: z.string().optional(),
93
+ })
94
+ .optional(),
95
+ iframe: z
96
+ .object({
97
+ width: z.number().optional(),
98
+ height: z.number().optional(),
99
+ src: z.url(),
100
+ })
101
+ .optional(),
102
+ });
103
+ /**
104
+ * Zod schema for HTML fragment content item.
105
+ * Represents raw HTML content to be embedded directly in the platform.
106
+ *
107
+ * @property type - Always 'html' for this content type
108
+ * @property html - HTML content to be embedded
109
+ *
110
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#html-fragment
111
+ */
112
+ export const HtmlSchema = BaseContentItemSchema.extend({
113
+ type: z.literal('html'),
114
+ html: z.string(),
115
+ });
116
+ /**
117
+ * Zod schema for file content item.
118
+ * Represents a downloadable file resource.
119
+ *
120
+ * @property type - Always 'file' for this content type
121
+ * @property url - URL to download the file
122
+ * @property mediaType - MIME type of the file
123
+ * @property expiresAt - Optional expiration timestamp for the file URL
124
+ *
125
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#file
126
+ */
127
+ export const FileSchema = BaseContentItemSchema.extend({
128
+ type: z.literal('file'),
129
+ url: z.url(),
130
+ mediaType: z.string(),
131
+ expiresAt: z.iso.datetime().optional(),
132
+ });
133
+ /**
134
+ * Zod schema for image content item.
135
+ * Represents an image resource with optional dimensions.
136
+ *
137
+ * @property type - Always 'image' for this content type
138
+ * @property url - URL to the image
139
+ * @property width - Optional image width in pixels
140
+ * @property height - Optional image height in pixels
141
+ *
142
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#image
143
+ */
144
+ export const ImageSchema = BaseContentItemSchema.extend({
145
+ type: z.literal('image'),
146
+ url: z.url(),
147
+ width: z.number().optional(),
148
+ height: z.number().optional(),
149
+ });
150
+ /**
151
+ * Zod schema for validating any Deep Linking content item type.
152
+ * Uses discriminated union on the 'type' field to determine the specific content item schema.
153
+ * Supports: ltiResourceLink, link, html, file, and image content types.
154
+ *
155
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
156
+ */
157
+ export const ContentItemSchema = z.discriminatedUnion('type', [
158
+ LtiResourceLinkSchema,
159
+ LinkSchema,
160
+ HtmlSchema,
161
+ FileSchema,
162
+ ImageSchema,
163
+ ]);
@@ -33,8 +33,8 @@ declare const LTIDeepLinkingMessageSchema: z.ZodObject<{
33
33
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
34
34
  file: "file";
35
35
  ltiResourceLink: "ltiResourceLink";
36
- html: "html";
37
36
  link: "link";
37
+ html: "html";
38
38
  image: "image";
39
39
  }>>>;
40
40
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -60,8 +60,8 @@ export declare const LTIMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
60
60
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
61
61
  file: "file";
62
62
  ltiResourceLink: "ltiResourceLink";
63
- html: "html";
64
63
  link: "link";
64
+ html: "html";
65
65
  image: "image";
66
66
  }>>>;
67
67
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -86,8 +86,8 @@ export declare const LTIMessagesArraySchema: z.ZodArray<z.ZodDiscriminatedUnion<
86
86
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
87
87
  file: "file";
88
88
  ltiResourceLink: "ltiResourceLink";
89
- html: "html";
90
89
  link: "link";
90
+ html: "html";
91
91
  image: "image";
92
92
  }>>>;
93
93
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -57,8 +57,8 @@ export declare const RegistrationResponseSchema: z.ZodObject<{
57
57
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
58
58
  file: "file";
59
59
  ltiResourceLink: "ltiResourceLink";
60
- html: "html";
61
60
  link: "link";
61
+ html: "html";
62
62
  image: "image";
63
63
  }>>>;
64
64
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -28,8 +28,8 @@ declare const LTIToolConfigurationSchema: z.ZodObject<{
28
28
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
29
29
  file: "file";
30
30
  ltiResourceLink: "ltiResourceLink";
31
- html: "html";
32
31
  link: "link";
32
+ html: "html";
33
33
  image: "image";
34
34
  }>>>;
35
35
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -87,8 +87,8 @@ export declare const ToolRegistrationPayloadSchema: z.ZodObject<{
87
87
  supported_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
88
88
  file: "file";
89
89
  ltiResourceLink: "ltiResourceLink";
90
- html: "html";
91
90
  link: "link";
91
+ html: "html";
92
92
  image: "image";
93
93
  }>>>;
94
94
  supported_media_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -0,0 +1,56 @@
1
+ import type { BaseLogger } from 'pino';
2
+ import type { LTISession } from '../interfaces/ltiSession.js';
3
+ import type { DeepLinkingContentItem } from '../schemas/lti13/deepLinking/contentItem.schema.js';
4
+ /**
5
+ * Deep Linking service for LTI 1.3.
6
+ * Generates signed JWT responses containing selected content items to return to the platform.
7
+ *
8
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
9
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
10
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0
11
+ */
12
+ export declare class DeepLinkingService {
13
+ private keyPair;
14
+ private logger;
15
+ private keyId;
16
+ constructor(keyPair: CryptoKeyPair, logger: BaseLogger, keyId?: string);
17
+ /**
18
+ * Creates a Deep Linking response with selected content items.
19
+ * Generates a signed JWT and returns an HTML form that auto-submits to the platform.
20
+ *
21
+ * @param session - Active LTI session containing Deep Linking configuration
22
+ * @param contentItems - Array of content items selected by the user
23
+ * @returns HTML string containing auto-submit form
24
+ * @throws {Error} When Deep Linking is not available for the session
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const html = await deepLinkingService.createResponse(session, [
29
+ * {
30
+ * type: 'ltiResourceLink',
31
+ * title: 'Quiz 1',
32
+ * url: 'https://tool.example.com/quiz/1'
33
+ * }
34
+ * ]);
35
+ * // Returns HTML form that auto-submits to platform
36
+ * ```
37
+ */
38
+ createResponse(session: LTISession, contentItems: DeepLinkingContentItem[]): Promise<string>;
39
+ /**
40
+ * Creates a signed JWT containing the Deep Linking response payload.
41
+ *
42
+ * @param session - Active LTI session with Deep Linking configuration
43
+ * @param contentItems - Array of selected content items
44
+ * @returns Signed JWT string
45
+ */
46
+ private createDeepLinkingJWT;
47
+ /**
48
+ * Generates an HTML form that auto-submits the Deep Linking response to the platform.
49
+ *
50
+ * @param returnUrl - Platform's Deep Linking return URL
51
+ * @param jwt - Signed JWT containing the response
52
+ * @returns HTML string with auto-submit form
53
+ */
54
+ private generateAutoSubmitForm;
55
+ }
56
+ //# sourceMappingURL=deepLinking.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepLinking.service.d.ts","sourceRoot":"","sources":["../../src/services/deepLinking.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AAEjG;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,KAAK;gBAFL,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,UAAU,EAClB,KAAK,SAAS;IAGxB;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,cAAc,CAClB,OAAO,EAAE,UAAU,EACnB,YAAY,EAAE,sBAAsB,EAAE,GACrC,OAAO,CAAC,MAAM,CAAC;IAiBlB;;;;;;OAMG;YACW,oBAAoB;IA6BlC;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;CAiB/B"}
@@ -0,0 +1,104 @@
1
+ import { SignJWT } from 'jose';
2
+ /**
3
+ * Deep Linking service for LTI 1.3.
4
+ * Generates signed JWT responses containing selected content items to return to the platform.
5
+ *
6
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
7
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
8
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0
9
+ */
10
+ export class DeepLinkingService {
11
+ keyPair;
12
+ logger;
13
+ keyId;
14
+ constructor(keyPair, logger, keyId = 'main') {
15
+ this.keyPair = keyPair;
16
+ this.logger = logger;
17
+ this.keyId = keyId;
18
+ }
19
+ /**
20
+ * Creates a Deep Linking response with selected content items.
21
+ * Generates a signed JWT and returns an HTML form that auto-submits to the platform.
22
+ *
23
+ * @param session - Active LTI session containing Deep Linking configuration
24
+ * @param contentItems - Array of content items selected by the user
25
+ * @returns HTML string containing auto-submit form
26
+ * @throws {Error} When Deep Linking is not available for the session
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const html = await deepLinkingService.createResponse(session, [
31
+ * {
32
+ * type: 'ltiResourceLink',
33
+ * title: 'Quiz 1',
34
+ * url: 'https://tool.example.com/quiz/1'
35
+ * }
36
+ * ]);
37
+ * // Returns HTML form that auto-submits to platform
38
+ * ```
39
+ */
40
+ async createResponse(session, contentItems) {
41
+ if (!session.services?.deepLinking) {
42
+ throw new Error('Deep Linking not available for this session');
43
+ }
44
+ this.logger.debug({
45
+ contentItemCount: contentItems.length,
46
+ returnUrl: session.services.deepLinking.returnUrl,
47
+ }, 'Creating Deep Linking response');
48
+ const jwt = await this.createDeepLinkingJWT(session, contentItems);
49
+ return this.generateAutoSubmitForm(session.services.deepLinking.returnUrl, jwt);
50
+ }
51
+ /**
52
+ * Creates a signed JWT containing the Deep Linking response payload.
53
+ *
54
+ * @param session - Active LTI session with Deep Linking configuration
55
+ * @param contentItems - Array of selected content items
56
+ * @returns Signed JWT string
57
+ */
58
+ async createDeepLinkingJWT(session, contentItems) {
59
+ const deepLinking = session.services.deepLinking;
60
+ const payload = {
61
+ iss: session.platform.clientId,
62
+ aud: session.platform.issuer,
63
+ exp: Math.floor(Date.now() / 1000) + 600,
64
+ iat: Math.floor(Date.now() / 1000),
65
+ nonce: crypto.randomUUID(),
66
+ 'https://purl.imsglobal.org/spec/lti/claim/message_type': 'LtiDeepLinkingResponse',
67
+ 'https://purl.imsglobal.org/spec/lti/claim/version': '1.3.0',
68
+ 'https://purl.imsglobal.org/spec/lti/claim/deployment_id': session.platform.deploymentId,
69
+ 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items': contentItems,
70
+ 'https://purl.imsglobal.org/spec/lti-dl/claim/data': deepLinking.data,
71
+ };
72
+ return await new SignJWT(payload)
73
+ .setProtectedHeader({
74
+ alg: 'RS256',
75
+ typ: 'JWT',
76
+ kid: this.keyId,
77
+ })
78
+ .sign(this.keyPair.privateKey);
79
+ }
80
+ /**
81
+ * Generates an HTML form that auto-submits the Deep Linking response to the platform.
82
+ *
83
+ * @param returnUrl - Platform's Deep Linking return URL
84
+ * @param jwt - Signed JWT containing the response
85
+ * @returns HTML string with auto-submit form
86
+ */
87
+ generateAutoSubmitForm(returnUrl, jwt) {
88
+ return `
89
+ <!DOCTYPE html>
90
+ <html>
91
+ <head>
92
+ <title>Returning to platform...</title>
93
+ </head>
94
+ <body>
95
+ <form id="deepLinkingForm" method="POST" action="${returnUrl}">
96
+ <input type="hidden" name="JWT" value="${jwt}" />
97
+ </form>
98
+ <script>
99
+ document.getElementById('deepLinkingForm').submit();
100
+ </script>
101
+ </body>
102
+ </html>`;
103
+ }
104
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lti-tool/core",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
4
4
  "description": "LTI 1.3 implementation for Node.js",
5
5
  "keywords": [
6
6
  "lti",
package/src/ltiTool.ts CHANGED
@@ -27,12 +27,14 @@ import {
27
27
  } from './schemas/lti13/ags/lineItem.schema.js';
28
28
  import { type Results, ResultsSchema } from './schemas/lti13/ags/result.schema.js';
29
29
  import { type ScoreSubmission } from './schemas/lti13/ags/scoreSubmission.schema.js';
30
+ import { type DeepLinkingContentItem } from './schemas/lti13/deepLinking/contentItem.schema.js';
30
31
  import { type OpenIDConfiguration } from './schemas/lti13/dynamicRegistration/openIDConfiguration.schema.js';
31
32
  import {
32
33
  type Member,
33
34
  NRPSContextMembershipResponseSchema,
34
35
  } from './schemas/lti13/nrps/contextMembership.schema.js';
35
36
  import { AGSService } from './services/ags.service.js';
37
+ import { DeepLinkingService } from './services/deepLinking.service.js';
36
38
  import { DynamicRegistrationService } from './services/dynamicRegistration.service.js';
37
39
  import { NRPSService } from './services/nrps.service.js';
38
40
  import { createSession } from './services/session.service.js';
@@ -69,6 +71,7 @@ export class LTITool {
69
71
  private tokenService: TokenService;
70
72
  private agsService: AGSService;
71
73
  private nrpsService: NRPSService;
74
+ private deepLinkingService: DeepLinkingService;
72
75
  private dynamicRegistrationService?: DynamicRegistrationService;
73
76
 
74
77
  /**
@@ -96,6 +99,11 @@ export class LTITool {
96
99
  this.config.storage,
97
100
  this.logger,
98
101
  );
102
+ this.deepLinkingService = new DeepLinkingService(
103
+ this.config.keyPair,
104
+ this.logger,
105
+ this.config.security?.keyId ?? 'main',
106
+ );
99
107
  if (this.config.dynamicRegistration) {
100
108
  this.dynamicRegistrationService = new DynamicRegistrationService(
101
109
  this.config.storage,
@@ -474,6 +482,41 @@ export class LTITool {
474
482
  }));
475
483
  }
476
484
 
485
+ /**
486
+ * Creates a Deep Linking response with selected content items.
487
+ * Generates a signed JWT and returns HTML form that auto-submits to the platform.
488
+ *
489
+ * @param session - Active LTI session containing Deep Linking configuration
490
+ * @param contentItems - Array of content items selected by the user
491
+ * @returns HTML string containing auto-submit form
492
+ * @throws {Error} When Deep Linking is not available for the session
493
+ *
494
+ * @example
495
+ * ```typescript
496
+ * const html = await ltiTool.createDeepLinkingResponse(session, [
497
+ * {
498
+ * type: 'ltiResourceLink',
499
+ * title: 'Quiz 1',
500
+ * url: 'https://tool.example.com/quiz/1'
501
+ * }
502
+ * ]);
503
+ * // Render the HTML to return content items to platform
504
+ * ```
505
+ */
506
+ async createDeepLinkingResponse(
507
+ session: LTISession,
508
+ contentItems: DeepLinkingContentItem[],
509
+ ): Promise<string> {
510
+ if (!session) {
511
+ throw new Error('session is required');
512
+ }
513
+ if (!contentItems) {
514
+ throw new Error('contentItems is required');
515
+ }
516
+
517
+ return await this.deepLinkingService.createResponse(session, contentItems);
518
+ }
519
+
477
520
  /**
478
521
  * Fetches and validates the OpenID Connect configuration from an LTI platform during dynamic registration.
479
522
  * Validates that the OIDC endpoint and issuer have matching hostnames for security.
@@ -1,4 +1,13 @@
1
1
  export { SessionIdSchema } from './common.schema.js';
2
+ export {
3
+ ContentItemSchema,
4
+ type DeepLinkingContentItem,
5
+ type DeepLinkingFile,
6
+ type DeepLinkingHtml,
7
+ type DeepLinkingImage,
8
+ type DeepLinkingLink,
9
+ type DeepLinkingLtiResourceLink,
10
+ } from './lti13/deepLinking/contentItem.schema.js';
2
11
  export {
3
12
  DynamicRegistrationFormSchema,
4
13
  type DynamicRegistrationForm,
@@ -0,0 +1,206 @@
1
+ import * as z from 'zod';
2
+
3
+ /**
4
+ * Zod schema for base content item properties shared across all content item types.
5
+ * Contains common metadata fields like title, text, icon, and thumbnail.
6
+ *
7
+ * @property title - Optional human-readable title for the content item
8
+ * @property text - Optional descriptive text for the content item
9
+ * @property icon - Optional icon image with URL and dimensions
10
+ * @property thumbnail - Optional thumbnail image with URL and dimensions
11
+ *
12
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
13
+ */
14
+ const BaseContentItemSchema = z.object({
15
+ title: z.string().optional(),
16
+ text: z.string().optional(),
17
+ icon: z
18
+ .object({
19
+ url: z.url(),
20
+ width: z.number().optional(),
21
+ height: z.number().optional(),
22
+ })
23
+ .optional(),
24
+ thumbnail: z
25
+ .object({
26
+ url: z.url(),
27
+ width: z.number().optional(),
28
+ height: z.number().optional(),
29
+ })
30
+ .optional(),
31
+ });
32
+
33
+ /**
34
+ * Zod schema for LTI Resource Link content item.
35
+ * Represents a launchable LTI tool resource that can be embedded in the platform.
36
+ *
37
+ * @property type - Always 'ltiResourceLink' for this content type
38
+ * @property url - Optional launch URL for the resource
39
+ * @property custom - Optional custom parameters passed to the tool on launch
40
+ * @property lineItem - Optional gradebook column configuration for this resource
41
+ * @property available - Optional availability window with start/end dates
42
+ * @property submission - Optional submission window with start/end dates
43
+ *
44
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#lti-resource-link
45
+ */
46
+ export const LtiResourceLinkSchema = BaseContentItemSchema.extend({
47
+ type: z.literal('ltiResourceLink'),
48
+ url: z.url().optional(),
49
+ custom: z.record(z.string(), z.string()).optional(),
50
+ lineItem: z
51
+ .object({
52
+ scoreMaximum: z.number().min(0),
53
+ label: z.string(),
54
+ resourceId: z.string().optional(),
55
+ tag: z.string().optional(),
56
+ })
57
+ .optional(),
58
+ available: z
59
+ .object({
60
+ startDateTime: z.iso.datetime().optional(),
61
+ endDateTime: z.iso.datetime().optional(),
62
+ })
63
+ .optional(),
64
+ submission: z
65
+ .object({
66
+ startDateTime: z.iso.datetime().optional(),
67
+ endDateTime: z.iso.datetime().optional(),
68
+ })
69
+ .optional(),
70
+ });
71
+
72
+ /**
73
+ * Zod schema for simple link content item.
74
+ * Represents a standard web link that can be opened in various ways.
75
+ *
76
+ * @property type - Always 'link' for this content type
77
+ * @property url - Target URL for the link
78
+ * @property embed - Optional HTML embed code
79
+ * @property window - Optional window configuration for opening the link
80
+ * @property iframe - Optional iframe configuration for embedding the link
81
+ *
82
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#link
83
+ */
84
+ export const LinkSchema = BaseContentItemSchema.extend({
85
+ type: z.literal('link'),
86
+ url: z.url(),
87
+ embed: z
88
+ .object({
89
+ html: z.string(),
90
+ })
91
+ .optional(),
92
+ window: z
93
+ .object({
94
+ targetName: z.string().optional(),
95
+ windowFeatures: z.string().optional(),
96
+ })
97
+ .optional(),
98
+ iframe: z
99
+ .object({
100
+ width: z.number().optional(),
101
+ height: z.number().optional(),
102
+ src: z.url(),
103
+ })
104
+ .optional(),
105
+ });
106
+
107
+ /**
108
+ * Zod schema for HTML fragment content item.
109
+ * Represents raw HTML content to be embedded directly in the platform.
110
+ *
111
+ * @property type - Always 'html' for this content type
112
+ * @property html - HTML content to be embedded
113
+ *
114
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#html-fragment
115
+ */
116
+ export const HtmlSchema = BaseContentItemSchema.extend({
117
+ type: z.literal('html'),
118
+ html: z.string(),
119
+ });
120
+
121
+ /**
122
+ * Zod schema for file content item.
123
+ * Represents a downloadable file resource.
124
+ *
125
+ * @property type - Always 'file' for this content type
126
+ * @property url - URL to download the file
127
+ * @property mediaType - MIME type of the file
128
+ * @property expiresAt - Optional expiration timestamp for the file URL
129
+ *
130
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#file
131
+ */
132
+ export const FileSchema = BaseContentItemSchema.extend({
133
+ type: z.literal('file'),
134
+ url: z.url(),
135
+ mediaType: z.string(),
136
+ expiresAt: z.iso.datetime().optional(),
137
+ });
138
+
139
+ /**
140
+ * Zod schema for image content item.
141
+ * Represents an image resource with optional dimensions.
142
+ *
143
+ * @property type - Always 'image' for this content type
144
+ * @property url - URL to the image
145
+ * @property width - Optional image width in pixels
146
+ * @property height - Optional image height in pixels
147
+ *
148
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#image
149
+ */
150
+ export const ImageSchema = BaseContentItemSchema.extend({
151
+ type: z.literal('image'),
152
+ url: z.url(),
153
+ width: z.number().optional(),
154
+ height: z.number().optional(),
155
+ });
156
+
157
+ /**
158
+ * Zod schema for validating any Deep Linking content item type.
159
+ * Uses discriminated union on the 'type' field to determine the specific content item schema.
160
+ * Supports: ltiResourceLink, link, html, file, and image content types.
161
+ *
162
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0#content-item-types
163
+ */
164
+ export const ContentItemSchema = z.discriminatedUnion('type', [
165
+ LtiResourceLinkSchema,
166
+ LinkSchema,
167
+ HtmlSchema,
168
+ FileSchema,
169
+ ImageSchema,
170
+ ]);
171
+
172
+ /**
173
+ * Type representing a validated LTI Resource Link content item.
174
+ * Used for creating launchable LTI tool resources.
175
+ */
176
+ export type DeepLinkingLtiResourceLink = z.infer<typeof LtiResourceLinkSchema>;
177
+
178
+ /**
179
+ * Type representing a validated simple link content item.
180
+ * Used for creating standard web links.
181
+ */
182
+ export type DeepLinkingLink = z.infer<typeof LinkSchema>;
183
+
184
+ /**
185
+ * Type representing a validated HTML fragment content item.
186
+ * Used for embedding raw HTML content.
187
+ */
188
+ export type DeepLinkingHtml = z.infer<typeof HtmlSchema>;
189
+
190
+ /**
191
+ * Type representing a validated file content item.
192
+ * Used for linking to downloadable files.
193
+ */
194
+ export type DeepLinkingFile = z.infer<typeof FileSchema>;
195
+
196
+ /**
197
+ * Type representing a validated image content item.
198
+ * Used for embedding images.
199
+ */
200
+ export type DeepLinkingImage = z.infer<typeof ImageSchema>;
201
+
202
+ /**
203
+ * Type representing any validated Deep Linking content item.
204
+ * Can be any of: DeepLinkingLtiResourceLink, DeepLinkingLink, DeepLinkingHtml, DeepLinkingFile, or DeepLinkingImage.
205
+ */
206
+ export type DeepLinkingContentItem = z.infer<typeof ContentItemSchema>;
@@ -0,0 +1,123 @@
1
+ import { SignJWT } from 'jose';
2
+ import type { BaseLogger } from 'pino';
3
+
4
+ import type { LTISession } from '../interfaces/ltiSession.js';
5
+ import type { DeepLinkingContentItem } from '../schemas/lti13/deepLinking/contentItem.schema.js';
6
+
7
+ /**
8
+ * Deep Linking service for LTI 1.3.
9
+ * Generates signed JWT responses containing selected content items to return to the platform.
10
+ *
11
+ * @param keyPair - RSA key pair for signing client assertion JWTs (must be RS256 compatible)
12
+ * @param keyId - Key identifier for JWT header, should match JWKS key ID (defaults to 'main')
13
+ * @see https://www.imsglobal.org/spec/lti-dl/v2p0
14
+ */
15
+ export class DeepLinkingService {
16
+ constructor(
17
+ private keyPair: CryptoKeyPair,
18
+ private logger: BaseLogger,
19
+ private keyId = 'main',
20
+ ) {}
21
+
22
+ /**
23
+ * Creates a Deep Linking response with selected content items.
24
+ * Generates a signed JWT and returns an HTML form that auto-submits to the platform.
25
+ *
26
+ * @param session - Active LTI session containing Deep Linking configuration
27
+ * @param contentItems - Array of content items selected by the user
28
+ * @returns HTML string containing auto-submit form
29
+ * @throws {Error} When Deep Linking is not available for the session
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const html = await deepLinkingService.createResponse(session, [
34
+ * {
35
+ * type: 'ltiResourceLink',
36
+ * title: 'Quiz 1',
37
+ * url: 'https://tool.example.com/quiz/1'
38
+ * }
39
+ * ]);
40
+ * // Returns HTML form that auto-submits to platform
41
+ * ```
42
+ */
43
+ async createResponse(
44
+ session: LTISession,
45
+ contentItems: DeepLinkingContentItem[],
46
+ ): Promise<string> {
47
+ if (!session.services?.deepLinking) {
48
+ throw new Error('Deep Linking not available for this session');
49
+ }
50
+
51
+ this.logger.debug(
52
+ {
53
+ contentItemCount: contentItems.length,
54
+ returnUrl: session.services.deepLinking.returnUrl,
55
+ },
56
+ 'Creating Deep Linking response',
57
+ );
58
+
59
+ const jwt = await this.createDeepLinkingJWT(session, contentItems);
60
+ return this.generateAutoSubmitForm(session.services.deepLinking.returnUrl, jwt);
61
+ }
62
+
63
+ /**
64
+ * Creates a signed JWT containing the Deep Linking response payload.
65
+ *
66
+ * @param session - Active LTI session with Deep Linking configuration
67
+ * @param contentItems - Array of selected content items
68
+ * @returns Signed JWT string
69
+ */
70
+ private async createDeepLinkingJWT(
71
+ session: LTISession,
72
+ contentItems: DeepLinkingContentItem[],
73
+ ): Promise<string> {
74
+ const deepLinking = session.services!.deepLinking!;
75
+
76
+ const payload = {
77
+ iss: session.platform.clientId,
78
+ aud: session.platform.issuer,
79
+ exp: Math.floor(Date.now() / 1000) + 600,
80
+ iat: Math.floor(Date.now() / 1000),
81
+ nonce: crypto.randomUUID(),
82
+ 'https://purl.imsglobal.org/spec/lti/claim/message_type': 'LtiDeepLinkingResponse',
83
+ 'https://purl.imsglobal.org/spec/lti/claim/version': '1.3.0',
84
+ 'https://purl.imsglobal.org/spec/lti/claim/deployment_id':
85
+ session.platform.deploymentId,
86
+ 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items': contentItems,
87
+ 'https://purl.imsglobal.org/spec/lti-dl/claim/data': deepLinking.data,
88
+ };
89
+
90
+ return await new SignJWT(payload)
91
+ .setProtectedHeader({
92
+ alg: 'RS256',
93
+ typ: 'JWT',
94
+ kid: this.keyId,
95
+ })
96
+ .sign(this.keyPair.privateKey);
97
+ }
98
+
99
+ /**
100
+ * Generates an HTML form that auto-submits the Deep Linking response to the platform.
101
+ *
102
+ * @param returnUrl - Platform's Deep Linking return URL
103
+ * @param jwt - Signed JWT containing the response
104
+ * @returns HTML string with auto-submit form
105
+ */
106
+ private generateAutoSubmitForm(returnUrl: string, jwt: string): string {
107
+ return `
108
+ <!DOCTYPE html>
109
+ <html>
110
+ <head>
111
+ <title>Returning to platform...</title>
112
+ </head>
113
+ <body>
114
+ <form id="deepLinkingForm" method="POST" action="${returnUrl}">
115
+ <input type="hidden" name="JWT" value="${jwt}" />
116
+ </form>
117
+ <script>
118
+ document.getElementById('deepLinkingForm').submit();
119
+ </script>
120
+ </body>
121
+ </html>`;
122
+ }
123
+ }