@lti-tool/core 0.11.1 → 0.12.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 +6 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/index.d.ts.map +1 -1
- package/dist/interfaces/ltiConfig.d.ts +23 -0
- package/dist/interfaces/ltiConfig.d.ts.map +1 -1
- package/dist/interfaces/ltiDynamicRegistrationSession.d.ts +14 -0
- package/dist/interfaces/ltiDynamicRegistrationSession.d.ts.map +1 -0
- package/dist/interfaces/ltiDynamicRegistrationSession.js +1 -0
- package/dist/interfaces/ltiStorage.d.ts +22 -0
- package/dist/interfaces/ltiStorage.d.ts.map +1 -1
- package/dist/ltiTool.d.ts +64 -1
- package/dist/ltiTool.d.ts.map +1 -1
- package/dist/ltiTool.js +87 -1
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +4 -0
- package/dist/schemas/lti13/dynamicRegistration/ltiDynamicRegistration.schema.d.ts +34 -0
- package/dist/schemas/lti13/dynamicRegistration/ltiDynamicRegistration.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/dynamicRegistration/ltiDynamicRegistration.schema.js +28 -0
- package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.d.ts +101 -0
- package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/dynamicRegistration/ltiMessages.schema.js +51 -0
- package/dist/schemas/lti13/dynamicRegistration/openIDConfiguration.schema.d.ts +59 -0
- package/dist/schemas/lti13/dynamicRegistration/openIDConfiguration.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/dynamicRegistration/openIDConfiguration.schema.js +53 -0
- package/dist/schemas/lti13/dynamicRegistration/registrationRequest.schema.d.ts +7 -0
- package/dist/schemas/lti13/dynamicRegistration/registrationRequest.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/dynamicRegistration/registrationRequest.schema.js +5 -0
- package/dist/schemas/lti13/dynamicRegistration/registrationResponse.schema.d.ts +73 -0
- package/dist/schemas/lti13/dynamicRegistration/registrationResponse.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/dynamicRegistration/registrationResponse.schema.js +61 -0
- package/dist/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.d.ts +103 -0
- package/dist/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.js +49 -0
- package/dist/services/dynamicRegistration.service.d.ts +121 -0
- package/dist/services/dynamicRegistration.service.d.ts.map +1 -0
- package/dist/services/dynamicRegistration.service.js +312 -0
- package/dist/services/dynamicRegistrationHandlers/moodle.d.ts +48 -0
- package/dist/services/dynamicRegistrationHandlers/moodle.d.ts.map +1 -0
- package/dist/services/dynamicRegistrationHandlers/moodle.js +152 -0
- package/dist/utils/ltiPlatformCapabilities.d.ts +65 -0
- package/dist/utils/ltiPlatformCapabilities.d.ts.map +1 -0
- package/dist/utils/ltiPlatformCapabilities.js +73 -0
- package/package.json +1 -1
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/ltiConfig.ts +25 -0
- package/src/interfaces/ltiDynamicRegistrationSession.ts +14 -0
- package/src/interfaces/ltiStorage.ts +32 -0
- package/src/ltiTool.ts +122 -1
- package/src/schemas/index.ts +17 -0
- package/src/schemas/lti13/dynamicRegistration/ltiDynamicRegistration.schema.ts +31 -0
- package/src/schemas/lti13/dynamicRegistration/ltiMessages.schema.ts +59 -0
- package/src/schemas/lti13/dynamicRegistration/openIDConfiguration.schema.ts +60 -0
- package/src/schemas/lti13/dynamicRegistration/registrationRequest.schema.ts +8 -0
- package/src/schemas/lti13/dynamicRegistration/registrationResponse.schema.ts +67 -0
- package/src/schemas/lti13/dynamicRegistration/toolRegistrationPayload.schema.ts +55 -0
- package/src/services/dynamicRegistration.service.ts +406 -0
- package/src/services/dynamicRegistrationHandlers/moodle.ts +184 -0
- package/src/utils/ltiPlatformCapabilities.ts +86 -0
package/src/interfaces/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type { LTIClient } from './ltiClient.js';
|
|
2
2
|
export type { LTIConfig } from './ltiConfig.js';
|
|
3
3
|
export type { LTIDeployment } from './ltiDeployment.js';
|
|
4
|
+
export type { LTIDynamicRegistrationSession } from './ltiDynamicRegistrationSession.js';
|
|
4
5
|
export type { LTILaunchConfig } from './ltiLaunchConfig.js';
|
|
5
6
|
export type { LTISession } from './ltiSession.js';
|
|
6
7
|
export type { LTIStorage } from './ltiStorage.js';
|
|
@@ -2,6 +2,28 @@ import type { Logger } from 'pino';
|
|
|
2
2
|
|
|
3
3
|
import type { LTIStorage } from './ltiStorage.js';
|
|
4
4
|
|
|
5
|
+
/** Dynamic registration configuration for LTI 1.3 tool registration */
|
|
6
|
+
export interface DynamicRegistrationConfig {
|
|
7
|
+
/** Base URL of the LTI tool (e.g., 'https://my-tool.com') */
|
|
8
|
+
url: string;
|
|
9
|
+
/** Display name shown to users in the LMS (e.g., 'My Learning Tool') */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Optional description of the tool's functionality */
|
|
12
|
+
description?: string;
|
|
13
|
+
/** Optional URL to tool logo image for LMS display */
|
|
14
|
+
logo?: string;
|
|
15
|
+
/** Additional redirect URIs beyond the default /lti/launch endpoint */
|
|
16
|
+
redirectUris?: string[];
|
|
17
|
+
/** Optional custom deep linking content selection endpoint (defaults to {url}/lti/deep-linking) */
|
|
18
|
+
deepLinkingUri?: string;
|
|
19
|
+
/** Optional custom login endpoint (defaults to {url}/lti/login) */
|
|
20
|
+
loginUri?: string;
|
|
21
|
+
/** Optional custom launch endpoint (defaults to {url}/lti/launch) */
|
|
22
|
+
launchUri?: string;
|
|
23
|
+
/** Optional custom JWKS endpoint (defaults to {url}/lti/jwks) */
|
|
24
|
+
jwksUri?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
5
27
|
/**
|
|
6
28
|
* Configuration object for initializing an LTI Tool instance.
|
|
7
29
|
* Contains cryptographic keys, secrets, and storage adapter.
|
|
@@ -28,4 +50,7 @@ export interface LTIConfig {
|
|
|
28
50
|
/** Nonce expiration time in seconds (defaults to 600 = 10 minutes) */
|
|
29
51
|
nonceExpirationSeconds?: number;
|
|
30
52
|
};
|
|
53
|
+
|
|
54
|
+
/** Dynamic registration configuration for LTI 1.3 tool registration */
|
|
55
|
+
dynamicRegistration?: DynamicRegistrationConfig;
|
|
31
56
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OpenIDConfiguration } from '../schemas/lti13/dynamicRegistration/openIDConfiguration.schema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Temporary session data stored during LTI 1.3 dynamic registration flow.
|
|
5
|
+
* Used to maintain state between the registration initiation and completion steps.
|
|
6
|
+
*/
|
|
7
|
+
export interface LTIDynamicRegistrationSession {
|
|
8
|
+
/** Platform's OpenID Connect configuration retrieved during registration initiation */
|
|
9
|
+
openIdConfiguration: OpenIDConfiguration;
|
|
10
|
+
/** Registration token provided by the platform for this registration attempt */
|
|
11
|
+
registrationToken?: string;
|
|
12
|
+
/** Unix timestamp (milliseconds) when this session expires and should be cleaned up */
|
|
13
|
+
expiresAt: number;
|
|
14
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LTIClient } from './ltiClient.js';
|
|
2
2
|
import type { LTIDeployment } from './ltiDeployment.js';
|
|
3
|
+
import type { LTIDynamicRegistrationSession } from './ltiDynamicRegistrationSession.js';
|
|
3
4
|
import type { LTILaunchConfig } from './ltiLaunchConfig.js';
|
|
4
5
|
import type { LTISession } from './ltiSession.js';
|
|
5
6
|
|
|
@@ -158,4 +159,35 @@ export interface LTIStorage {
|
|
|
158
159
|
* @param launchConfig - Complete launch configuration with auth URLs and keys
|
|
159
160
|
*/
|
|
160
161
|
saveLaunchConfig(launchConfig: LTILaunchConfig): Promise<void>;
|
|
162
|
+
|
|
163
|
+
// Dynamic Registration
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Stores a temporary registration session during LTI 1.3 dynamic registration flow.
|
|
167
|
+
* Sessions have a TTL and are automatically cleaned up when expired.
|
|
168
|
+
*
|
|
169
|
+
* @param sessionId - Unique session identifier (typically a UUID)
|
|
170
|
+
* @param session - Registration session data including platform config and tokens
|
|
171
|
+
*/
|
|
172
|
+
setRegistrationSession(
|
|
173
|
+
sessionId: string,
|
|
174
|
+
session: LTIDynamicRegistrationSession,
|
|
175
|
+
): Promise<void>;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Retrieves a registration session by its ID for validation during completion.
|
|
179
|
+
*
|
|
180
|
+
* @param sessionId - Unique session identifier
|
|
181
|
+
* @returns Registration session if found and not expired, undefined otherwise
|
|
182
|
+
*/
|
|
183
|
+
getRegistrationSession(
|
|
184
|
+
sessionId: string,
|
|
185
|
+
): Promise<LTIDynamicRegistrationSession | undefined>;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Removes a registration session from storage (cleanup after completion or expiration).
|
|
189
|
+
*
|
|
190
|
+
* @param sessionId - Unique session identifier to delete
|
|
191
|
+
*/
|
|
192
|
+
deleteRegistrationSession(sessionId: string): Promise<void>;
|
|
161
193
|
}
|
package/src/ltiTool.ts
CHANGED
|
@@ -5,12 +5,15 @@ import type { JWKS } from './interfaces/jwks.js';
|
|
|
5
5
|
import type { LTIClient } from './interfaces/ltiClient.js';
|
|
6
6
|
import type { LTIConfig } from './interfaces/ltiConfig.js';
|
|
7
7
|
import type { LTIDeployment } from './interfaces/ltiDeployment.js';
|
|
8
|
+
import type { LTIDynamicRegistrationSession } from './interfaces/ltiDynamicRegistrationSession.js';
|
|
8
9
|
import type { LTISession } from './interfaces/ltiSession.js';
|
|
9
10
|
import { AddClientSchema, UpdateClientSchema } from './schemas/client.schema.js';
|
|
10
11
|
import {
|
|
12
|
+
type DynamicRegistrationForm,
|
|
11
13
|
HandleLoginParamsSchema,
|
|
12
14
|
type LTI13JwtPayload,
|
|
13
15
|
LTI13JwtPayloadSchema,
|
|
16
|
+
type RegistrationRequest,
|
|
14
17
|
SessionIdSchema,
|
|
15
18
|
VerifyLaunchParamsSchema,
|
|
16
19
|
} from './schemas/index.js';
|
|
@@ -24,11 +27,13 @@ import {
|
|
|
24
27
|
} from './schemas/lti13/ags/lineItem.schema.js';
|
|
25
28
|
import { type Results, ResultsSchema } from './schemas/lti13/ags/result.schema.js';
|
|
26
29
|
import { type ScoreSubmission } from './schemas/lti13/ags/scoreSubmission.schema.js';
|
|
30
|
+
import { type OpenIDConfiguration } from './schemas/lti13/dynamicRegistration/openIDConfiguration.schema.js';
|
|
27
31
|
import {
|
|
28
32
|
type Member,
|
|
29
33
|
NRPSContextMembershipResponseSchema,
|
|
30
34
|
} from './schemas/lti13/nrps/contextMembership.schema.js';
|
|
31
35
|
import { AGSService } from './services/ags.service.js';
|
|
36
|
+
import { DynamicRegistrationService } from './services/dynamicRegistration.service.js';
|
|
32
37
|
import { NRPSService } from './services/nrps.service.js';
|
|
33
38
|
import { createSession } from './services/session.service.js';
|
|
34
39
|
import { TokenService } from './services/token.service.js';
|
|
@@ -64,6 +69,7 @@ export class LTITool {
|
|
|
64
69
|
private tokenService: TokenService;
|
|
65
70
|
private agsService: AGSService;
|
|
66
71
|
private nrpsService: NRPSService;
|
|
72
|
+
private dynamicRegistrationService?: DynamicRegistrationService;
|
|
67
73
|
|
|
68
74
|
/**
|
|
69
75
|
* Creates a new LTI Tool instance.
|
|
@@ -90,6 +96,13 @@ export class LTITool {
|
|
|
90
96
|
this.config.storage,
|
|
91
97
|
this.logger,
|
|
92
98
|
);
|
|
99
|
+
if (this.config.dynamicRegistration) {
|
|
100
|
+
this.dynamicRegistrationService = new DynamicRegistrationService(
|
|
101
|
+
this.config.storage,
|
|
102
|
+
this.config.dynamicRegistration,
|
|
103
|
+
this.logger,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
/**
|
|
@@ -444,7 +457,6 @@ export class LTITool {
|
|
|
444
457
|
|
|
445
458
|
const response = await this.nrpsService.getMembers(session);
|
|
446
459
|
const data = await response.json();
|
|
447
|
-
console.log(data);
|
|
448
460
|
const validated = NRPSContextMembershipResponseSchema.parse(data);
|
|
449
461
|
|
|
450
462
|
// Transform to clean camelCase format
|
|
@@ -462,6 +474,76 @@ export class LTITool {
|
|
|
462
474
|
}));
|
|
463
475
|
}
|
|
464
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Fetches and validates the OpenID Connect configuration from an LTI platform during dynamic registration.
|
|
479
|
+
* Validates that the OIDC endpoint and issuer have matching hostnames for security.
|
|
480
|
+
*
|
|
481
|
+
* @param registrationRequest - Registration request containing openid_configuration URL and optional registration_token
|
|
482
|
+
* @returns Validated OpenID configuration with platform endpoints and supported features
|
|
483
|
+
* @throws {Error} When the configuration fetch fails, validation fails, or hostname mismatch detected
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* ```typescript
|
|
487
|
+
* const config = await ltiTool.fetchPlatformConfiguration({
|
|
488
|
+
* openid_configuration: 'https://platform.edu/.well-known/openid_configuration',
|
|
489
|
+
* registration_token: 'optional-bearer-token'
|
|
490
|
+
* });
|
|
491
|
+
* console.log('Platform issuer:', config.issuer);
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
async fetchPlatformConfiguration(
|
|
495
|
+
registrationRequest: RegistrationRequest,
|
|
496
|
+
): Promise<OpenIDConfiguration> {
|
|
497
|
+
if (!this.dynamicRegistrationService) {
|
|
498
|
+
throw new Error('Dynamic registration service is not configured');
|
|
499
|
+
}
|
|
500
|
+
return await this.dynamicRegistrationService.fetchPlatformConfiguration(
|
|
501
|
+
registrationRequest,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Initiates LTI 1.3 dynamic registration by fetching platform configuration and generating registration form.
|
|
507
|
+
* Creates a temporary session and returns vendor-specific HTML form for service selection.
|
|
508
|
+
*
|
|
509
|
+
* @param registrationRequest - Registration request containing openid_configuration URL and optional registration_token
|
|
510
|
+
* @param requestPath - Current request path used to build form action URLs
|
|
511
|
+
* @returns HTML form for service selection and registration completion
|
|
512
|
+
* @throws {Error} When dynamic registration service is not configured or platform configuration fails
|
|
513
|
+
*/
|
|
514
|
+
async initiateDynamicRegistration(
|
|
515
|
+
registrationRequest: RegistrationRequest,
|
|
516
|
+
requestPath: string,
|
|
517
|
+
): Promise<string> {
|
|
518
|
+
if (!this.dynamicRegistrationService) {
|
|
519
|
+
throw new Error('Dynamic registration service is not configured');
|
|
520
|
+
}
|
|
521
|
+
return await this.dynamicRegistrationService.initiateDynamicRegistration(
|
|
522
|
+
registrationRequest,
|
|
523
|
+
requestPath,
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Completes LTI 1.3 dynamic registration by processing form submission and storing client configuration.
|
|
529
|
+
* Validates session, registers with platform, stores client/deployment data, and returns success page.
|
|
530
|
+
*
|
|
531
|
+
* @param dynamicRegistrationForm - Validated form data containing selected services and session token
|
|
532
|
+
* @returns HTML success page with registration details and close button
|
|
533
|
+
* @throws {Error} When dynamic registration service is not configured or registration process fails
|
|
534
|
+
*/
|
|
535
|
+
async completeDynamicRegistration(
|
|
536
|
+
dynamicRegistrationForm: DynamicRegistrationForm,
|
|
537
|
+
): Promise<string> {
|
|
538
|
+
if (!this.dynamicRegistrationService) {
|
|
539
|
+
throw new Error('Dynmaic registration service is not configured');
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return await this.dynamicRegistrationService.completeDynamicRegistration(
|
|
543
|
+
dynamicRegistrationForm,
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
465
547
|
// Client management
|
|
466
548
|
|
|
467
549
|
/**
|
|
@@ -581,4 +663,43 @@ export class LTITool {
|
|
|
581
663
|
async deleteDeployment(clientId: string, deploymentId: string): Promise<void> {
|
|
582
664
|
return await this.config.storage.deleteDeployment(clientId, deploymentId);
|
|
583
665
|
}
|
|
666
|
+
|
|
667
|
+
// Dynamic Registration Session Management
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Stores a temporary registration session during LTI 1.3 dynamic registration flow.
|
|
671
|
+
* Sessions automatically expire after the configured TTL period.
|
|
672
|
+
*
|
|
673
|
+
* @param sessionId - Unique session identifier (typically a UUID)
|
|
674
|
+
* @param session - Registration session data including platform config and tokens
|
|
675
|
+
*/
|
|
676
|
+
async setRegistrationSession(
|
|
677
|
+
sessionId: string,
|
|
678
|
+
session: LTIDynamicRegistrationSession,
|
|
679
|
+
): Promise<void> {
|
|
680
|
+
return await this.config.storage.setRegistrationSession(sessionId, session);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Retrieves a registration session by its ID for validation during completion.
|
|
685
|
+
* Returns undefined if the session is not found or has expired.
|
|
686
|
+
*
|
|
687
|
+
* @param sessionId - Unique session identifier
|
|
688
|
+
* @returns Registration session if found and not expired, undefined otherwise
|
|
689
|
+
*/
|
|
690
|
+
async getRegistrationSession(
|
|
691
|
+
sessionId: string,
|
|
692
|
+
): Promise<LTIDynamicRegistrationSession | undefined> {
|
|
693
|
+
return await this.config.storage.getRegistrationSession(sessionId);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Removes a registration session from storage after completion or expiration.
|
|
698
|
+
* Used for cleanup to prevent session accumulation.
|
|
699
|
+
*
|
|
700
|
+
* @param sessionId - Unique session identifier to delete
|
|
701
|
+
*/
|
|
702
|
+
async deleteRegistrationSession(sessionId: string): Promise<void> {
|
|
703
|
+
return await this.config.storage.deleteRegistrationSession(sessionId);
|
|
704
|
+
}
|
|
584
705
|
}
|
package/src/schemas/index.ts
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
export { SessionIdSchema } from './common.schema.js';
|
|
2
|
+
export {
|
|
3
|
+
DynamicRegistrationFormSchema,
|
|
4
|
+
type DynamicRegistrationForm,
|
|
5
|
+
} from './lti13/dynamicRegistration/ltiDynamicRegistration.schema.js';
|
|
6
|
+
export {
|
|
7
|
+
LTIMessagesArraySchema,
|
|
8
|
+
type LTIMessage,
|
|
9
|
+
} from './lti13/dynamicRegistration/ltiMessages.schema.js';
|
|
10
|
+
export { type OpenIDConfiguration } from './lti13/dynamicRegistration/openIDConfiguration.schema.js';
|
|
11
|
+
export {
|
|
12
|
+
RegistrationRequestSchema,
|
|
13
|
+
type RegistrationRequest,
|
|
14
|
+
} from './lti13/dynamicRegistration/registrationRequest.schema.js';
|
|
15
|
+
export {
|
|
16
|
+
RegistrationResponseSchema,
|
|
17
|
+
type RegistrationResponse,
|
|
18
|
+
} from './lti13/dynamicRegistration/registrationResponse.schema.js';
|
|
2
19
|
export {
|
|
3
20
|
LTI13JwtPayloadSchema,
|
|
4
21
|
type LTI13JwtPayload,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for validating LTI 1.3 dynamic registration form submissions.
|
|
5
|
+
* Represents the service selections and session data submitted by an administrator during tool registration.
|
|
6
|
+
*
|
|
7
|
+
* @property services - Optional array of LTI Advantage services the admin chooses to enable:
|
|
8
|
+
* - 'ags': Assignment and Grade Services for grade passback
|
|
9
|
+
* - 'nrps': Names and Role Provisioning Services for roster access
|
|
10
|
+
* - 'deep_linking': Deep Linking for content selection
|
|
11
|
+
* - 'tool_settings': Tool Settings service for configuration storage
|
|
12
|
+
* - 'basic_outcome': Basic Outcome service for simple grade reporting
|
|
13
|
+
* @property sessionToken - Security token to validate the registration session and prevent CSRF attacks
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const formData = {
|
|
18
|
+
* services: ['ags', 'nrps', 'deep_linking'],
|
|
19
|
+
* sessionToken: 'uuid-session-token'
|
|
20
|
+
* };
|
|
21
|
+
* const validated = DynamicRegistrationFormSchema.parse(formData);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const DynamicRegistrationFormSchema = z.object({
|
|
25
|
+
services: z
|
|
26
|
+
.array(z.enum(['ags', 'nrps', 'deep_linking', 'tool_settings', 'basic_outcome']))
|
|
27
|
+
.optional(),
|
|
28
|
+
sessionToken: z.string(),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export type DynamicRegistrationForm = z.infer<typeof DynamicRegistrationFormSchema>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for LTI 1.3 Resource Link Request message configuration.
|
|
5
|
+
* Represents the standard tool launch message type for accessing tool content.
|
|
6
|
+
* This is the most common LTI message type for regular tool launches.
|
|
7
|
+
*/
|
|
8
|
+
const LTIResourceLinkMessageSchema = z.object({
|
|
9
|
+
type: z.literal('LtiResourceLinkRequest'),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Zod schema for LTI 1.3 Deep Linking Request message configuration.
|
|
14
|
+
* Represents the message type used when the tool supports content selection/authoring.
|
|
15
|
+
* Allows instructors to select or create content that will be linked back to the LMS.
|
|
16
|
+
*
|
|
17
|
+
* @property type - Always 'LtiDeepLinkingRequest' for deep linking messages
|
|
18
|
+
* @property target_link_uri - Optional URL where deep linking requests should be sent
|
|
19
|
+
* @property label - Optional human-readable label for the deep linking option
|
|
20
|
+
* @property placements - Optional array of where the tool can be placed in the LMS UI
|
|
21
|
+
* @property supported_types - Optional array of content types the tool can create/select
|
|
22
|
+
* @property supported_media_types - Optional array of MIME types the tool supports
|
|
23
|
+
* @property roles - Optional array of LIS role URIs that can access deep linking
|
|
24
|
+
* @property custom_parameters - Optional custom parameters for deep linking configuration
|
|
25
|
+
*/
|
|
26
|
+
const LTIDeepLinkingMessageSchema = z.object({
|
|
27
|
+
type: z.literal('LtiDeepLinkingRequest'),
|
|
28
|
+
target_link_uri: z.url().optional(),
|
|
29
|
+
label: z.string().optional(),
|
|
30
|
+
placements: z
|
|
31
|
+
.array(z.enum(['ContentArea', 'RichTextEditor', 'CourseNavigation']))
|
|
32
|
+
.optional(),
|
|
33
|
+
supported_types: z
|
|
34
|
+
.array(z.enum(['ltiResourceLink', 'file', 'html', 'link', 'image']))
|
|
35
|
+
.optional(),
|
|
36
|
+
supported_media_types: z.array(z.string()).optional(), // e.g., ['image/*', 'video/*']
|
|
37
|
+
roles: z.array(z.string()).optional(), // LIS role URIs
|
|
38
|
+
custom_parameters: z.record(z.string(), z.string()).optional(),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Discriminated union schema for all supported LTI 1.3 message types.
|
|
43
|
+
* Used during dynamic registration to declare which message types the tool supports.
|
|
44
|
+
* The platform uses this to determine what launch options to provide to users.
|
|
45
|
+
*/
|
|
46
|
+
export const LTIMessageSchema = z.discriminatedUnion('type', [
|
|
47
|
+
LTIResourceLinkMessageSchema,
|
|
48
|
+
LTIDeepLinkingMessageSchema,
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Schema for validating arrays of LTI message configurations.
|
|
53
|
+
* Used in tool registration payloads to declare multiple supported message types.
|
|
54
|
+
*/
|
|
55
|
+
export const LTIMessagesArraySchema = z.array(LTIMessageSchema);
|
|
56
|
+
|
|
57
|
+
export type LTIMessage = z.infer<typeof LTIMessageSchema>;
|
|
58
|
+
export type LTIResourceLinkMessage = z.infer<typeof LTIResourceLinkMessageSchema>;
|
|
59
|
+
export type LTIDeepLinkingMessage = z.infer<typeof LTIDeepLinkingMessageSchema>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for LTI platform-specific configuration within OpenID Connect Discovery.
|
|
5
|
+
* Contains LTI-specific metadata about the platform's capabilities and supported features.
|
|
6
|
+
*
|
|
7
|
+
* @property product_family_code - Platform identifier (e.g., 'moodle', 'canvas', 'sakai')
|
|
8
|
+
* @property version - Platform version string
|
|
9
|
+
* @property messages_supported - Array of LTI message types the platform supports
|
|
10
|
+
* @property variables - Optional array of custom variable names the platform supports
|
|
11
|
+
*/
|
|
12
|
+
export const ltiPlatformConfigurationSchema = z.object({
|
|
13
|
+
product_family_code: z.string(),
|
|
14
|
+
version: z.string(),
|
|
15
|
+
messages_supported: z.array(
|
|
16
|
+
z
|
|
17
|
+
.object({
|
|
18
|
+
type: z.string(),
|
|
19
|
+
placements: z.array(z.string()).optional(),
|
|
20
|
+
})
|
|
21
|
+
.loose(),
|
|
22
|
+
),
|
|
23
|
+
variables: z.array(z.string()).optional(),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Zod schema for validating OpenID Connect Discovery configuration from LTI 1.3 platforms.
|
|
28
|
+
* This configuration is fetched during dynamic registration to discover platform endpoints,
|
|
29
|
+
* supported features, and security requirements. Used to validate the response from the
|
|
30
|
+
* platform's /.well-known/openid_configuration endpoint.
|
|
31
|
+
*
|
|
32
|
+
* @property issuer - Platform's issuer URL (must match hostname of configuration endpoint)
|
|
33
|
+
* @property authorization_endpoint - OAuth 2.0 authorization endpoint for LTI launches
|
|
34
|
+
* @property registration_endpoint - Dynamic registration endpoint for tool registration
|
|
35
|
+
* @property jwks_uri - JSON Web Key Set endpoint for signature verification
|
|
36
|
+
* @property token_endpoint - OAuth 2.0 token endpoint for service access tokens
|
|
37
|
+
* @property scopes_supported - Array of OAuth scopes the platform supports (AGS, NRPS, etc.)
|
|
38
|
+
* @property https://purl.imsglobal.org/spec/lti-platform-configuration - LTI-specific platform metadata
|
|
39
|
+
*/
|
|
40
|
+
export const openIDConfigurationSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
issuer: z.url(),
|
|
43
|
+
authorization_endpoint: z.url(),
|
|
44
|
+
registration_endpoint: z.url(),
|
|
45
|
+
jwks_uri: z.url(),
|
|
46
|
+
token_endpoint: z.url(),
|
|
47
|
+
token_endpoint_auth_methods_supported: z.array(z.string()),
|
|
48
|
+
token_endpoint_auth_signing_alg_values_supported: z.array(z.string()),
|
|
49
|
+
scopes_supported: z.array(z.string()),
|
|
50
|
+
response_types_supported: z.array(z.string()),
|
|
51
|
+
id_token_signing_alg_values_supported: z.array(z.string()),
|
|
52
|
+
claims_supported: z.array(z.string()),
|
|
53
|
+
subject_types_supported: z.array(z.string()),
|
|
54
|
+
authorization_server: z.string().optional(),
|
|
55
|
+
'https://purl.imsglobal.org/spec/lti-platform-configuration':
|
|
56
|
+
ltiPlatformConfigurationSchema,
|
|
57
|
+
})
|
|
58
|
+
.loose();
|
|
59
|
+
|
|
60
|
+
export type OpenIDConfiguration = z.infer<typeof openIDConfigurationSchema>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
|
|
3
|
+
import { LTIMessagesArraySchema } from './ltiMessages.schema';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Zod schema for LTI tool configuration within dynamic registration response.
|
|
7
|
+
* Contains tool-specific metadata returned by the platform after successful registration.
|
|
8
|
+
*
|
|
9
|
+
* @property domain - Tool's domain name for security validation
|
|
10
|
+
* @property target_link_uri - Optional default launch URL for the tool
|
|
11
|
+
* @property custom_parameters - Optional custom parameters passed to the tool
|
|
12
|
+
* @property claims - Array of JWT claims the tool requires (e.g., 'iss', 'sub', 'name', 'email')
|
|
13
|
+
* @property messages - Array of LTI message types the tool supports
|
|
14
|
+
* @property version - Optional tool version string
|
|
15
|
+
* @property deployment_id - Optional deployment identifier assigned by the platform
|
|
16
|
+
*/
|
|
17
|
+
const LTIToolConfigurationResponseSchema = z.object({
|
|
18
|
+
domain: z.string(),
|
|
19
|
+
target_link_uri: z.url().optional(),
|
|
20
|
+
custom_parameters: z.record(z.string(), z.string()).optional(),
|
|
21
|
+
claims: z.array(z.string()),
|
|
22
|
+
messages: LTIMessagesArraySchema,
|
|
23
|
+
version: z.string().optional(),
|
|
24
|
+
deployment_id: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Zod schema for validating LTI 1.3 dynamic registration response from platforms.
|
|
29
|
+
* This response is returned after successfully registering a tool with an LTI platform.
|
|
30
|
+
* Contains the registered client credentials and configuration that the tool needs to store.
|
|
31
|
+
*
|
|
32
|
+
* @property client_id - Unique client identifier assigned by the platform
|
|
33
|
+
* @property registration_client_uri - Optional URI for managing this registration
|
|
34
|
+
* @property registration_access_token - Optional token for registration management
|
|
35
|
+
* @property application_type - Always 'web' for LTI tools
|
|
36
|
+
* @property response_types - OAuth response types, always ['id_token'] for LTI
|
|
37
|
+
* @property grant_types - OAuth grant types supported ('implicit', 'client_credentials')
|
|
38
|
+
* @property initiate_login_uri - Tool's login initiation endpoint
|
|
39
|
+
* @property redirect_uris - Array of valid redirect URIs for the tool
|
|
40
|
+
* @property client_name - Human-readable name of the registered tool
|
|
41
|
+
* @property jwks_uri - Tool's JSON Web Key Set endpoint for signature verification
|
|
42
|
+
* @property logo_uri - Optional URL to the tool's logo image
|
|
43
|
+
* @property token_endpoint_auth_method - Always 'private_key_jwt' for LTI 1.3
|
|
44
|
+
* @property contacts - Optional array of contact email addresses
|
|
45
|
+
* @property scope - Optional OAuth scopes granted to the tool
|
|
46
|
+
* @property https://purl.imsglobal.org/spec/lti-tool-configuration - LTI-specific tool configuration
|
|
47
|
+
*/
|
|
48
|
+
export const RegistrationResponseSchema = z.object({
|
|
49
|
+
client_id: z.string(),
|
|
50
|
+
registration_client_uri: z.url().optional(),
|
|
51
|
+
registration_access_token: z.string().optional(),
|
|
52
|
+
application_type: z.literal('web'),
|
|
53
|
+
response_types: z.array(z.literal('id_token')),
|
|
54
|
+
grant_types: z.array(z.enum(['implicit', 'client_credentials'])), // Note: "implicit" not "implict"
|
|
55
|
+
initiate_login_uri: z.url(),
|
|
56
|
+
redirect_uris: z.array(z.url()),
|
|
57
|
+
client_name: z.string(),
|
|
58
|
+
jwks_uri: z.url(),
|
|
59
|
+
logo_uri: z.url().optional().or(z.literal('')),
|
|
60
|
+
token_endpoint_auth_method: z.literal('private_key_jwt'),
|
|
61
|
+
contacts: z.array(z.email()).optional(),
|
|
62
|
+
scope: z.string().optional().or(z.literal('')),
|
|
63
|
+
'https://purl.imsglobal.org/spec/lti-tool-configuration':
|
|
64
|
+
LTIToolConfigurationResponseSchema,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export type RegistrationResponse = z.infer<typeof RegistrationResponseSchema>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
|
|
3
|
+
import { LTIMessagesArraySchema } from './ltiMessages.schema';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Zod schema for LTI tool configuration section within tool registration payload.
|
|
7
|
+
* Contains LTI-specific metadata about the tool being registered with the platform.
|
|
8
|
+
*
|
|
9
|
+
* @property domain - Tool's domain name for security validation and CORS policies
|
|
10
|
+
* @property description - Optional human-readable description of the tool's purpose
|
|
11
|
+
* @property target_link_uri - Default launch URL where the platform should send LTI requests
|
|
12
|
+
* @property claims - Array of JWT claims the tool requires from launch requests (e.g., 'iss', 'sub', 'name', 'email')
|
|
13
|
+
* @property messages - Array of LTI message types the tool supports (ResourceLink, DeepLinking, etc.)
|
|
14
|
+
*/
|
|
15
|
+
const LTIToolConfigurationSchema = z.object({
|
|
16
|
+
domain: z.string(),
|
|
17
|
+
description: z.string().optional(),
|
|
18
|
+
target_link_uri: z.url(),
|
|
19
|
+
claims: z.array(z.string()),
|
|
20
|
+
messages: LTIMessagesArraySchema,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Zod schema for validating LTI 1.3 dynamic registration payload sent to platforms.
|
|
25
|
+
* This payload is constructed by the tool and sent to the platform's registration endpoint
|
|
26
|
+
* to register the tool and request specific OAuth scopes and LTI services.
|
|
27
|
+
*
|
|
28
|
+
* @property application_type - Always 'web' for LTI tools
|
|
29
|
+
* @property response_types - OAuth response types, always ['id_token'] for LTI 1.3
|
|
30
|
+
* @property grant_types - OAuth grant types requested ('implicit' for launches, 'client_credentials' for services)
|
|
31
|
+
* @property initiate_login_uri - Tool's login initiation endpoint for OIDC flow
|
|
32
|
+
* @property redirect_uris - Array of valid redirect URIs where the platform can send responses
|
|
33
|
+
* @property client_name - Human-readable name of the tool being registered
|
|
34
|
+
* @property jwks_uri - Tool's JSON Web Key Set endpoint for signature verification
|
|
35
|
+
* @property logo_uri - Optional URL to the tool's logo image
|
|
36
|
+
* @property scope - Optional OAuth scopes being requested (AGS, NRPS, etc.)
|
|
37
|
+
* @property token_endpoint_auth_method - Always 'private_key_jwt' for LTI 1.3 security
|
|
38
|
+
* @property https://purl.imsglobal.org/spec/lti-tool-configuration - LTI-specific tool configuration
|
|
39
|
+
*/
|
|
40
|
+
export const ToolRegistrationPayloadSchema = z.object({
|
|
41
|
+
application_type: z.literal('web'),
|
|
42
|
+
response_types: z.array(z.literal('id_token')),
|
|
43
|
+
grant_types: z.array(z.enum(['implicit', 'client_credentials'])),
|
|
44
|
+
initiate_login_uri: z.url(),
|
|
45
|
+
redirect_uris: z.array(z.url()),
|
|
46
|
+
client_name: z.string(),
|
|
47
|
+
jwks_uri: z.url(),
|
|
48
|
+
logo_uri: z.url().optional(),
|
|
49
|
+
scope: z.string().optional(),
|
|
50
|
+
token_endpoint_auth_method: z.literal('private_key_jwt'),
|
|
51
|
+
'https://purl.imsglobal.org/spec/lti-tool-configuration': LTIToolConfigurationSchema,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export type ToolRegistrationPayload = z.infer<typeof ToolRegistrationPayloadSchema>;
|
|
55
|
+
export type LTIToolConfiguration = z.infer<typeof LTIToolConfigurationSchema>;
|