@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.
- package/CHANGELOG.md +15 -0
- package/README.md +60 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/interfaces/index.d.ts +7 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +1 -0
- package/dist/interfaces/jwks.d.ts +18 -0
- package/dist/interfaces/jwks.d.ts.map +1 -0
- package/dist/interfaces/jwks.js +1 -0
- package/dist/interfaces/ltiClient.d.ts +24 -0
- package/dist/interfaces/ltiClient.d.ts.map +1 -0
- package/dist/interfaces/ltiClient.js +1 -0
- package/dist/interfaces/ltiConfig.d.ts +26 -0
- package/dist/interfaces/ltiConfig.d.ts.map +1 -0
- package/dist/interfaces/ltiConfig.js +1 -0
- package/dist/interfaces/ltiDeployment.d.ts +15 -0
- package/dist/interfaces/ltiDeployment.d.ts.map +1 -0
- package/dist/interfaces/ltiDeployment.js +1 -0
- package/dist/interfaces/ltiLaunchConfig.d.ts +19 -0
- package/dist/interfaces/ltiLaunchConfig.d.ts.map +1 -0
- package/dist/interfaces/ltiLaunchConfig.js +1 -0
- package/dist/interfaces/ltiSession.d.ts +110 -0
- package/dist/interfaces/ltiSession.d.ts.map +1 -0
- package/dist/interfaces/ltiSession.js +1 -0
- package/dist/interfaces/ltiStorage.d.ts +122 -0
- package/dist/interfaces/ltiStorage.d.ts.map +1 -0
- package/dist/interfaces/ltiStorage.js +1 -0
- package/dist/ltiTool.d.ts +184 -0
- package/dist/ltiTool.d.ts.map +1 -0
- package/dist/ltiTool.js +305 -0
- package/dist/schemas/client.schema.d.ts +33 -0
- package/dist/schemas/client.schema.d.ts.map +1 -0
- package/dist/schemas/client.schema.js +14 -0
- package/dist/schemas/common.schema.d.ts +6 -0
- package/dist/schemas/common.schema.d.ts.map +1 -0
- package/dist/schemas/common.schema.js +5 -0
- package/dist/schemas/deployment.schema.d.ts +8 -0
- package/dist/schemas/deployment.schema.d.ts.map +1 -0
- package/dist/schemas/deployment.schema.js +11 -0
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +4 -0
- package/dist/schemas/lti13/ags/scoreSubmission.schema.d.ts +34 -0
- package/dist/schemas/lti13/ags/scoreSubmission.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/ags/scoreSubmission.schema.js +41 -0
- package/dist/schemas/lti13/claims/baseJwtClaims.schema.d.ts +11 -0
- package/dist/schemas/lti13/claims/baseJwtClaims.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/claims/baseJwtClaims.schema.js +10 -0
- package/dist/schemas/lti13/claims/contextClaims.schema.d.ts +11 -0
- package/dist/schemas/lti13/claims/contextClaims.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/claims/contextClaims.schema.js +14 -0
- package/dist/schemas/lti13/claims/coreLtiClaims.schema.d.ts +9 -0
- package/dist/schemas/lti13/claims/coreLtiClaims.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/claims/coreLtiClaims.schema.js +11 -0
- package/dist/schemas/lti13/claims/platformClaims.schema.d.ts +19 -0
- package/dist/schemas/lti13/claims/platformClaims.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/claims/platformClaims.schema.js +24 -0
- package/dist/schemas/lti13/claims/privacyClaims.schema.d.ts +8 -0
- package/dist/schemas/lti13/claims/privacyClaims.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/claims/privacyClaims.schema.js +7 -0
- package/dist/schemas/lti13/claims/serviceClaims.schema.d.ts +20 -0
- package/dist/schemas/lti13/claims/serviceClaims.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/claims/serviceClaims.schema.js +25 -0
- package/dist/schemas/lti13/lti13JwtPayload.schema.d.ts +66 -0
- package/dist/schemas/lti13/lti13JwtPayload.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/lti13JwtPayload.schema.js +22 -0
- package/dist/schemas/lti13/lti13Launch.schema.d.ts +14 -0
- package/dist/schemas/lti13/lti13Launch.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/lti13Launch.schema.js +13 -0
- package/dist/schemas/lti13/lti13Login.schema.d.ts +23 -0
- package/dist/schemas/lti13/lti13Login.schema.d.ts.map +1 -0
- package/dist/schemas/lti13/lti13Login.schema.js +16 -0
- package/dist/services/ags.service.d.ts +38 -0
- package/dist/services/ags.service.d.ts.map +1 -0
- package/dist/services/ags.service.js +69 -0
- package/dist/services/session.service.d.ts +11 -0
- package/dist/services/session.service.d.ts.map +1 -0
- package/dist/services/session.service.js +103 -0
- package/dist/services/token.service.d.ts +36 -0
- package/dist/services/token.service.d.ts.map +1 -0
- package/dist/services/token.service.js +74 -0
- package/dist/utils/launchConfigValidation.d.ts +3 -0
- package/dist/utils/launchConfigValidation.d.ts.map +1 -0
- package/dist/utils/launchConfigValidation.js +7 -0
- package/package.json +53 -0
- package/src/index.ts +3 -0
- package/src/interfaces/index.ts +6 -0
- package/src/interfaces/jwks.ts +20 -0
- package/src/interfaces/ltiClient.ts +24 -0
- package/src/interfaces/ltiConfig.ts +31 -0
- package/src/interfaces/ltiDeployment.ts +17 -0
- package/src/interfaces/ltiLaunchConfig.ts +23 -0
- package/src/interfaces/ltiSession.ts +119 -0
- package/src/interfaces/ltiStorage.ts +161 -0
- package/src/ltiTool.ts +394 -0
- package/src/schemas/client.schema.ts +17 -0
- package/src/schemas/common.schema.ts +7 -0
- package/src/schemas/deployment.schema.ts +12 -0
- package/src/schemas/index.ts +10 -0
- package/src/schemas/lti13/ags/scoreSubmission.schema.ts +54 -0
- package/src/schemas/lti13/claims/baseJwtClaims.schema.ts +11 -0
- package/src/schemas/lti13/claims/contextClaims.schema.ts +16 -0
- package/src/schemas/lti13/claims/coreLtiClaims.schema.ts +12 -0
- package/src/schemas/lti13/claims/platformClaims.schema.ts +27 -0
- package/src/schemas/lti13/claims/privacyClaims.schema.ts +8 -0
- package/src/schemas/lti13/claims/serviceClaims.schema.ts +28 -0
- package/src/schemas/lti13/lti13JwtPayload.schema.ts +36 -0
- package/src/schemas/lti13/lti13Launch.schema.ts +15 -0
- package/src/schemas/lti13/lti13Login.schema.ts +18 -0
- package/src/services/ags.service.ts +92 -0
- package/src/services/session.service.ts +115 -0
- package/src/services/token.service.ts +84 -0
- package/src/utils/launchConfigValidation.ts +16 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { JWKS } from './interfaces/jwks.js';
|
|
2
|
+
import type { LTIClient } from './interfaces/ltiClient.js';
|
|
3
|
+
import type { LTIConfig } from './interfaces/ltiConfig.js';
|
|
4
|
+
import type { LTIDeployment } from './interfaces/ltiDeployment.js';
|
|
5
|
+
import type { LTISession } from './interfaces/ltiSession.js';
|
|
6
|
+
import { type LTI13JwtPayload } from './schemas/index.js';
|
|
7
|
+
import type { ScoreSubmission } from './schemas/lti13/ags/scoreSubmission.schema.js';
|
|
8
|
+
/**
|
|
9
|
+
* Main LTI 1.3 Tool implementation providing secure authentication, launch verification,
|
|
10
|
+
* and LTI Advantage services integration.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const ltiTool = new LTITool({
|
|
15
|
+
* stateSecret: new TextEncoder().encode('your-secret'),
|
|
16
|
+
* keyPair: await generateKeyPair('RS256'),
|
|
17
|
+
* storage: new MemoryStorage()
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Handle login initiation
|
|
21
|
+
* const authUrl = await ltiTool.handleLogin({
|
|
22
|
+
* client_id: 'your-client-id',
|
|
23
|
+
* iss: 'https://platform.example.com',
|
|
24
|
+
* launchUrl: 'https://yourtool.com/lti/launch',
|
|
25
|
+
* login_hint: 'user123',
|
|
26
|
+
* target_link_uri: 'https://yourtool.com/content',
|
|
27
|
+
* lti_deployment_id: 'deployment123'
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class LTITool {
|
|
32
|
+
private config;
|
|
33
|
+
/** Cache for JWKS remote key sets to improve performance */
|
|
34
|
+
private jwksCache;
|
|
35
|
+
private logger;
|
|
36
|
+
private tokenService;
|
|
37
|
+
private agsService;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new LTI Tool instance.
|
|
40
|
+
*
|
|
41
|
+
* @param config - Configuration object containing secrets, keys, and storage adapter
|
|
42
|
+
*/
|
|
43
|
+
constructor(config: LTIConfig);
|
|
44
|
+
/**
|
|
45
|
+
* Handles LTI 1.3 login initiation by generating state/nonce and redirecting to platform auth.
|
|
46
|
+
*
|
|
47
|
+
* @param params - Login parameters from the platform
|
|
48
|
+
* @param params.client_id - OAuth2 client identifier for this tool
|
|
49
|
+
* @param params.iss - Platform issuer URL (identifies the LMS)
|
|
50
|
+
* @param params.launchUrl - URL where platform will POST the id_token after auth
|
|
51
|
+
* @param params.login_hint - Platform-specific user identifier hint
|
|
52
|
+
* @param params.target_link_uri - Final destination URL after successful launch
|
|
53
|
+
* @param params.lti_deployment_id - Deployment identifier within the platform
|
|
54
|
+
* @param params.lti_message_hint - Optional platform-specific message context
|
|
55
|
+
* @returns Authorization URL to redirect user to for authentication
|
|
56
|
+
* @throws {Error} When platform configuration is not found
|
|
57
|
+
*/
|
|
58
|
+
handleLogin(params: {
|
|
59
|
+
client_id: string;
|
|
60
|
+
iss: string;
|
|
61
|
+
launchUrl: URL | string;
|
|
62
|
+
login_hint: string;
|
|
63
|
+
target_link_uri: string;
|
|
64
|
+
lti_deployment_id: string;
|
|
65
|
+
lti_message_hint?: string;
|
|
66
|
+
}): Promise<string>;
|
|
67
|
+
/**
|
|
68
|
+
* Verifies and validates an LTI 1.3 launch by checking JWT signatures, nonces, and claims.
|
|
69
|
+
*
|
|
70
|
+
* Performs comprehensive security validation including:
|
|
71
|
+
* - JWT signature verification using platform's JWKS
|
|
72
|
+
* - State JWT verification to prevent CSRF
|
|
73
|
+
* - Nonce validation to prevent replay attacks
|
|
74
|
+
* - Client ID and deployment ID verification
|
|
75
|
+
* - LTI 1.3 claim structure validation
|
|
76
|
+
*
|
|
77
|
+
* @param idToken - JWT id_token received from platform after authentication
|
|
78
|
+
* @param state - State JWT that was generated during login initiation
|
|
79
|
+
* @returns Validated and parsed LTI 1.3 JWT payload
|
|
80
|
+
* @throws {Error} When verification fails for security reasons
|
|
81
|
+
*/
|
|
82
|
+
verifyLaunch(idToken: string, state: string): Promise<LTI13JwtPayload>;
|
|
83
|
+
/**
|
|
84
|
+
* Generates JSON Web Key Set (JWKS) containing the tool's public key for platform verification.
|
|
85
|
+
*
|
|
86
|
+
* @returns JWKS object with the tool's public key for JWT signature verification
|
|
87
|
+
*/
|
|
88
|
+
getJWKS(): Promise<JWKS>;
|
|
89
|
+
/**
|
|
90
|
+
* Creates and stores a new LTI session from validated JWT payload.
|
|
91
|
+
*
|
|
92
|
+
* @param lti13JwtPayload - Validated LTI 1.3 JWT payload from successful launch
|
|
93
|
+
* @returns Created session object with user, context, and service information
|
|
94
|
+
*/
|
|
95
|
+
createSession(lti13JwtPayload: LTI13JwtPayload): Promise<LTISession>;
|
|
96
|
+
/**
|
|
97
|
+
* Retrieves an existing LTI session by session ID.
|
|
98
|
+
*
|
|
99
|
+
* @param sessionId - Unique session identifier
|
|
100
|
+
* @returns Session object if found, undefined otherwise
|
|
101
|
+
*/
|
|
102
|
+
getSession(sessionId: string): Promise<LTISession | undefined>;
|
|
103
|
+
/**
|
|
104
|
+
* Submits a grade score to the platform using Assignment and Grade Services (AGS).
|
|
105
|
+
*
|
|
106
|
+
* @param session - Active LTI session containing AGS service endpoints
|
|
107
|
+
* @param score - Score submission data including grade value and user ID
|
|
108
|
+
* @returns Result of the score submission
|
|
109
|
+
* @throws {Error} When AGS is not available or submission fails
|
|
110
|
+
*/
|
|
111
|
+
submitScore(session: LTISession, score: ScoreSubmission): Promise<Response>;
|
|
112
|
+
/**
|
|
113
|
+
* Retrieves all configured LTI client platforms.
|
|
114
|
+
*
|
|
115
|
+
* @returns Array of client configurations (without deployment details)
|
|
116
|
+
*/
|
|
117
|
+
listClients(): Promise<Omit<LTIClient, 'deployments'>[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Updates an existing client configuration.
|
|
120
|
+
*
|
|
121
|
+
* @param clientId - Unique client identifier
|
|
122
|
+
* @param client - Partial client object with fields to update
|
|
123
|
+
*/
|
|
124
|
+
updateClient(clientId: string, client: Partial<Omit<LTIClient, 'id' | 'deployments'>>): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Retrieves a specific client configuration by ID.
|
|
127
|
+
*
|
|
128
|
+
* @param clientId - Unique client identifier
|
|
129
|
+
* @returns Client configuration if found, undefined otherwise
|
|
130
|
+
*/
|
|
131
|
+
getClientById(clientId: string): Promise<LTIClient | undefined>;
|
|
132
|
+
/**
|
|
133
|
+
* Adds a new LTI client platform configuration.
|
|
134
|
+
*
|
|
135
|
+
* @param client - Client configuration (ID will be auto-generated)
|
|
136
|
+
* @returns The generated client ID
|
|
137
|
+
*/
|
|
138
|
+
addClient(client: Omit<LTIClient, 'id' | 'deployments'>): Promise<string>;
|
|
139
|
+
/**
|
|
140
|
+
* Removes a client configuration and all its deployments.
|
|
141
|
+
*
|
|
142
|
+
* @param clientId - Unique client identifier
|
|
143
|
+
*/
|
|
144
|
+
deleteClient(clientId: string): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Lists all deployments for a specific client platform.
|
|
147
|
+
*
|
|
148
|
+
* @param clientId - Client identifier
|
|
149
|
+
* @returns Array of deployment configurations for the client
|
|
150
|
+
*/
|
|
151
|
+
listDeployments(clientId: string): Promise<LTIDeployment[]>;
|
|
152
|
+
/**
|
|
153
|
+
* Retrieves a specific deployment configuration.
|
|
154
|
+
*
|
|
155
|
+
* @param clientId - Client identifier
|
|
156
|
+
* @param deploymentId - Deployment identifier
|
|
157
|
+
* @returns Deployment configuration if found, undefined otherwise
|
|
158
|
+
*/
|
|
159
|
+
getDeployment(clientId: string, deploymentId: string): Promise<LTIDeployment | undefined>;
|
|
160
|
+
/**
|
|
161
|
+
* Adds a new deployment to an existing client.
|
|
162
|
+
*
|
|
163
|
+
* @param clientId - Client identifier
|
|
164
|
+
* @param deployment - Deployment configuration to add
|
|
165
|
+
* @returns The generated deployment ID
|
|
166
|
+
*/
|
|
167
|
+
addDeployment(clientId: string, deployment: Omit<LTIDeployment, 'id'>): Promise<string>;
|
|
168
|
+
/**
|
|
169
|
+
* Updates an existing deployment configuration.
|
|
170
|
+
*
|
|
171
|
+
* @param clientId - Client identifier
|
|
172
|
+
* @param deploymentId - Deployment identifier
|
|
173
|
+
* @param deployment - Partial deployment object with fields to update
|
|
174
|
+
*/
|
|
175
|
+
updateDeployment(clientId: string, deploymentId: string, deployment: Partial<LTIDeployment>): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Removes a deployment from a client.
|
|
178
|
+
*
|
|
179
|
+
* @param clientId - Client identifier
|
|
180
|
+
* @param deploymentId - Deployment identifier to remove
|
|
181
|
+
*/
|
|
182
|
+
deleteDeployment(clientId: string, deploymentId: string): Promise<void>;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=ltiTool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ltiTool.d.ts","sourceRoot":"","sources":["../src/ltiTool.ts"],"names":[],"mappings":"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"}
|
package/dist/ltiTool.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { SignJWT, createRemoteJWKSet, decodeJwt, exportJWK, jwtVerify } from 'jose';
|
|
2
|
+
import { AddClientSchema, UpdateClientSchema } from './schemas/client.schema.js';
|
|
3
|
+
import { HandleLoginParamsSchema, LTI13JwtPayloadSchema, SessionIdSchema, VerifyLaunchParamsSchema, } from './schemas/index.js';
|
|
4
|
+
import { AGSService } from './services/ags.service.js';
|
|
5
|
+
import { createSession } from './services/session.service.js';
|
|
6
|
+
import { TokenService } from './services/token.service.js';
|
|
7
|
+
import { getValidLaunchConfig } from './utils/launchConfigValidation.js';
|
|
8
|
+
/**
|
|
9
|
+
* Main LTI 1.3 Tool implementation providing secure authentication, launch verification,
|
|
10
|
+
* and LTI Advantage services integration.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const ltiTool = new LTITool({
|
|
15
|
+
* stateSecret: new TextEncoder().encode('your-secret'),
|
|
16
|
+
* keyPair: await generateKeyPair('RS256'),
|
|
17
|
+
* storage: new MemoryStorage()
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Handle login initiation
|
|
21
|
+
* const authUrl = await ltiTool.handleLogin({
|
|
22
|
+
* client_id: 'your-client-id',
|
|
23
|
+
* iss: 'https://platform.example.com',
|
|
24
|
+
* launchUrl: 'https://yourtool.com/lti/launch',
|
|
25
|
+
* login_hint: 'user123',
|
|
26
|
+
* target_link_uri: 'https://yourtool.com/content',
|
|
27
|
+
* lti_deployment_id: 'deployment123'
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class LTITool {
|
|
32
|
+
config;
|
|
33
|
+
/** Cache for JWKS remote key sets to improve performance */
|
|
34
|
+
jwksCache = new Map();
|
|
35
|
+
logger;
|
|
36
|
+
tokenService;
|
|
37
|
+
agsService;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new LTI Tool instance.
|
|
40
|
+
*
|
|
41
|
+
* @param config - Configuration object containing secrets, keys, and storage adapter
|
|
42
|
+
*/
|
|
43
|
+
constructor(config) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.logger =
|
|
46
|
+
config.logger ??
|
|
47
|
+
{
|
|
48
|
+
debug: () => { },
|
|
49
|
+
info: () => { },
|
|
50
|
+
warn: () => { },
|
|
51
|
+
error: () => { },
|
|
52
|
+
};
|
|
53
|
+
this.tokenService = new TokenService(this.config.keyPair, this.config.security?.keyId ?? 'main');
|
|
54
|
+
this.agsService = new AGSService(this.tokenService, this.config.storage, this.logger);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Handles LTI 1.3 login initiation by generating state/nonce and redirecting to platform auth.
|
|
58
|
+
*
|
|
59
|
+
* @param params - Login parameters from the platform
|
|
60
|
+
* @param params.client_id - OAuth2 client identifier for this tool
|
|
61
|
+
* @param params.iss - Platform issuer URL (identifies the LMS)
|
|
62
|
+
* @param params.launchUrl - URL where platform will POST the id_token after auth
|
|
63
|
+
* @param params.login_hint - Platform-specific user identifier hint
|
|
64
|
+
* @param params.target_link_uri - Final destination URL after successful launch
|
|
65
|
+
* @param params.lti_deployment_id - Deployment identifier within the platform
|
|
66
|
+
* @param params.lti_message_hint - Optional platform-specific message context
|
|
67
|
+
* @returns Authorization URL to redirect user to for authentication
|
|
68
|
+
* @throws {Error} When platform configuration is not found
|
|
69
|
+
*/
|
|
70
|
+
async handleLogin(params) {
|
|
71
|
+
const validatedParams = HandleLoginParamsSchema.parse(params);
|
|
72
|
+
const nonce = crypto.randomUUID();
|
|
73
|
+
// Store nonce with expiration for replay attack prevention
|
|
74
|
+
const nonceExpirationSeconds = this.config.security?.nonceExpirationSeconds ?? 600;
|
|
75
|
+
const nonceExpiresAt = new Date(Date.now() + nonceExpirationSeconds * 1000);
|
|
76
|
+
await this.config.storage.storeNonce(nonce, nonceExpiresAt);
|
|
77
|
+
const state = await new SignJWT({
|
|
78
|
+
nonce,
|
|
79
|
+
iss: validatedParams.iss,
|
|
80
|
+
client_id: validatedParams.client_id,
|
|
81
|
+
target_link_uri: validatedParams.target_link_uri,
|
|
82
|
+
exp: Math.floor(Date.now() / 1000) +
|
|
83
|
+
(this.config.security?.stateExpirationSeconds ?? 600),
|
|
84
|
+
})
|
|
85
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
86
|
+
.sign(this.config.stateSecret);
|
|
87
|
+
const launchConfig = await getValidLaunchConfig(this.config.storage, validatedParams.iss, validatedParams.client_id, validatedParams.lti_deployment_id);
|
|
88
|
+
const authUrl = new URL(launchConfig.authUrl);
|
|
89
|
+
authUrl.searchParams.set('scope', 'openid');
|
|
90
|
+
authUrl.searchParams.set('response_type', 'id_token');
|
|
91
|
+
authUrl.searchParams.set('response_mode', 'form_post');
|
|
92
|
+
authUrl.searchParams.set('prompt', 'none');
|
|
93
|
+
authUrl.searchParams.set('client_id', validatedParams.client_id);
|
|
94
|
+
authUrl.searchParams.set('redirect_uri', validatedParams.launchUrl.toString());
|
|
95
|
+
authUrl.searchParams.set('login_hint', validatedParams.login_hint);
|
|
96
|
+
authUrl.searchParams.set('state', state);
|
|
97
|
+
authUrl.searchParams.set('nonce', nonce);
|
|
98
|
+
authUrl.searchParams.set('lti_deployment_id', validatedParams.lti_deployment_id);
|
|
99
|
+
if (validatedParams.lti_message_hint) {
|
|
100
|
+
authUrl.searchParams.set('lti_message_hint', validatedParams.lti_message_hint);
|
|
101
|
+
}
|
|
102
|
+
return authUrl.toString();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Verifies and validates an LTI 1.3 launch by checking JWT signatures, nonces, and claims.
|
|
106
|
+
*
|
|
107
|
+
* Performs comprehensive security validation including:
|
|
108
|
+
* - JWT signature verification using platform's JWKS
|
|
109
|
+
* - State JWT verification to prevent CSRF
|
|
110
|
+
* - Nonce validation to prevent replay attacks
|
|
111
|
+
* - Client ID and deployment ID verification
|
|
112
|
+
* - LTI 1.3 claim structure validation
|
|
113
|
+
*
|
|
114
|
+
* @param idToken - JWT id_token received from platform after authentication
|
|
115
|
+
* @param state - State JWT that was generated during login initiation
|
|
116
|
+
* @returns Validated and parsed LTI 1.3 JWT payload
|
|
117
|
+
* @throws {Error} When verification fails for security reasons
|
|
118
|
+
*/
|
|
119
|
+
async verifyLaunch(idToken, state) {
|
|
120
|
+
const validatedParams = VerifyLaunchParamsSchema.parse({ idToken, state });
|
|
121
|
+
// 1. UNVERIFIED - get issuer
|
|
122
|
+
const unverified = decodeJwt(validatedParams.idToken);
|
|
123
|
+
if (!unverified.iss) {
|
|
124
|
+
throw new Error('No issuer in token');
|
|
125
|
+
}
|
|
126
|
+
// 2. get the launchConfig so we can get the remote JWKS from our data store
|
|
127
|
+
const launchConfig = await getValidLaunchConfig(this.config.storage, unverified.iss, unverified.aud, unverified['https://purl.imsglobal.org/spec/lti/claim/deployment_id']);
|
|
128
|
+
// 3. Verify LMS JWT
|
|
129
|
+
let jwks = this.jwksCache.get(launchConfig.jwksUrl);
|
|
130
|
+
if (!jwks) {
|
|
131
|
+
jwks = createRemoteJWKSet(new URL(launchConfig.jwksUrl));
|
|
132
|
+
this.jwksCache.set(launchConfig.jwksUrl, jwks);
|
|
133
|
+
}
|
|
134
|
+
const { payload } = await jwtVerify(validatedParams.idToken, jwks);
|
|
135
|
+
// 4. Verify our state JWT
|
|
136
|
+
const { payload: stateData } = await jwtVerify(validatedParams.state, this.config.stateSecret);
|
|
137
|
+
// 5. Parse and validate LMS JWT
|
|
138
|
+
const validated = LTI13JwtPayloadSchema.parse(payload);
|
|
139
|
+
// 6. Verify client id matches (audience claim)
|
|
140
|
+
if (validated.aud !== launchConfig.clientId) {
|
|
141
|
+
throw new Error(`Invalid client_id: expected ${launchConfig.clientId}, got ${validated.aud}`);
|
|
142
|
+
}
|
|
143
|
+
// 7. Verify nonce matches
|
|
144
|
+
if (stateData.nonce !== validated.nonce) {
|
|
145
|
+
throw new Error('Nonce mismatch');
|
|
146
|
+
}
|
|
147
|
+
// 8. Check nonce hasn't been used before (prevent replay attacks)
|
|
148
|
+
const isValidNonce = await this.config.storage.validateNonce(validated.nonce);
|
|
149
|
+
if (!isValidNonce) {
|
|
150
|
+
throw new Error('Nonce has already been used or expired');
|
|
151
|
+
}
|
|
152
|
+
return validated;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generates JSON Web Key Set (JWKS) containing the tool's public key for platform verification.
|
|
156
|
+
*
|
|
157
|
+
* @returns JWKS object with the tool's public key for JWT signature verification
|
|
158
|
+
*/
|
|
159
|
+
async getJWKS() {
|
|
160
|
+
const publicJwk = await exportJWK(this.config.keyPair.publicKey);
|
|
161
|
+
return {
|
|
162
|
+
keys: [
|
|
163
|
+
{
|
|
164
|
+
...publicJwk,
|
|
165
|
+
use: 'sig',
|
|
166
|
+
alg: 'RS256',
|
|
167
|
+
kid: this.config.security?.keyId ?? 'main',
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Creates and stores a new LTI session from validated JWT payload.
|
|
174
|
+
*
|
|
175
|
+
* @param lti13JwtPayload - Validated LTI 1.3 JWT payload from successful launch
|
|
176
|
+
* @returns Created session object with user, context, and service information
|
|
177
|
+
*/
|
|
178
|
+
async createSession(lti13JwtPayload) {
|
|
179
|
+
const session = createSession(lti13JwtPayload);
|
|
180
|
+
await this.config.storage.addSession(session);
|
|
181
|
+
return session;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Retrieves an existing LTI session by session ID.
|
|
185
|
+
*
|
|
186
|
+
* @param sessionId - Unique session identifier
|
|
187
|
+
* @returns Session object if found, undefined otherwise
|
|
188
|
+
*/
|
|
189
|
+
async getSession(sessionId) {
|
|
190
|
+
const validatedSessionId = SessionIdSchema.parse(sessionId);
|
|
191
|
+
return await this.config.storage.getSession(validatedSessionId);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Submits a grade score to the platform using Assignment and Grade Services (AGS).
|
|
195
|
+
*
|
|
196
|
+
* @param session - Active LTI session containing AGS service endpoints
|
|
197
|
+
* @param score - Score submission data including grade value and user ID
|
|
198
|
+
* @returns Result of the score submission
|
|
199
|
+
* @throws {Error} When AGS is not available or submission fails
|
|
200
|
+
*/
|
|
201
|
+
async submitScore(session, score) {
|
|
202
|
+
if (!session) {
|
|
203
|
+
throw new Error('session is required');
|
|
204
|
+
}
|
|
205
|
+
if (!score) {
|
|
206
|
+
throw new Error('score is required');
|
|
207
|
+
}
|
|
208
|
+
return await this.agsService.submitScore(session, score);
|
|
209
|
+
}
|
|
210
|
+
// Client management
|
|
211
|
+
/**
|
|
212
|
+
* Retrieves all configured LTI client platforms.
|
|
213
|
+
*
|
|
214
|
+
* @returns Array of client configurations (without deployment details)
|
|
215
|
+
*/
|
|
216
|
+
async listClients() {
|
|
217
|
+
return await this.config.storage.listClients();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Updates an existing client configuration.
|
|
221
|
+
*
|
|
222
|
+
* @param clientId - Unique client identifier
|
|
223
|
+
* @param client - Partial client object with fields to update
|
|
224
|
+
*/
|
|
225
|
+
async updateClient(clientId, client) {
|
|
226
|
+
const validated = UpdateClientSchema.parse(client);
|
|
227
|
+
return await this.config.storage.updateClient(clientId, validated);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Retrieves a specific client configuration by ID.
|
|
231
|
+
*
|
|
232
|
+
* @param clientId - Unique client identifier
|
|
233
|
+
* @returns Client configuration if found, undefined otherwise
|
|
234
|
+
*/
|
|
235
|
+
async getClientById(clientId) {
|
|
236
|
+
return await this.config.storage.getClientById(clientId);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Adds a new LTI client platform configuration.
|
|
240
|
+
*
|
|
241
|
+
* @param client - Client configuration (ID will be auto-generated)
|
|
242
|
+
* @returns The generated client ID
|
|
243
|
+
*/
|
|
244
|
+
async addClient(client) {
|
|
245
|
+
const validated = AddClientSchema.parse(client);
|
|
246
|
+
return await this.config.storage.addClient(validated);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Removes a client configuration and all its deployments.
|
|
250
|
+
*
|
|
251
|
+
* @param clientId - Unique client identifier
|
|
252
|
+
*/
|
|
253
|
+
async deleteClient(clientId) {
|
|
254
|
+
return await this.config.storage.deleteClient(clientId);
|
|
255
|
+
}
|
|
256
|
+
// Deployment management
|
|
257
|
+
/**
|
|
258
|
+
* Lists all deployments for a specific client platform.
|
|
259
|
+
*
|
|
260
|
+
* @param clientId - Client identifier
|
|
261
|
+
* @returns Array of deployment configurations for the client
|
|
262
|
+
*/
|
|
263
|
+
async listDeployments(clientId) {
|
|
264
|
+
return await this.config.storage.listDeployments(clientId);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Retrieves a specific deployment configuration.
|
|
268
|
+
*
|
|
269
|
+
* @param clientId - Client identifier
|
|
270
|
+
* @param deploymentId - Deployment identifier
|
|
271
|
+
* @returns Deployment configuration if found, undefined otherwise
|
|
272
|
+
*/
|
|
273
|
+
async getDeployment(clientId, deploymentId) {
|
|
274
|
+
return await this.config.storage.getDeployment(clientId, deploymentId);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Adds a new deployment to an existing client.
|
|
278
|
+
*
|
|
279
|
+
* @param clientId - Client identifier
|
|
280
|
+
* @param deployment - Deployment configuration to add
|
|
281
|
+
* @returns The generated deployment ID
|
|
282
|
+
*/
|
|
283
|
+
async addDeployment(clientId, deployment) {
|
|
284
|
+
return await this.config.storage.addDeployment(clientId, deployment);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Updates an existing deployment configuration.
|
|
288
|
+
*
|
|
289
|
+
* @param clientId - Client identifier
|
|
290
|
+
* @param deploymentId - Deployment identifier
|
|
291
|
+
* @param deployment - Partial deployment object with fields to update
|
|
292
|
+
*/
|
|
293
|
+
async updateDeployment(clientId, deploymentId, deployment) {
|
|
294
|
+
return await this.config.storage.updateDeployment(clientId, deploymentId, deployment);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Removes a deployment from a client.
|
|
298
|
+
*
|
|
299
|
+
* @param clientId - Client identifier
|
|
300
|
+
* @param deploymentId - Deployment identifier to remove
|
|
301
|
+
*/
|
|
302
|
+
async deleteDeployment(clientId, deploymentId) {
|
|
303
|
+
return await this.config.storage.deleteDeployment(clientId, deploymentId);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ClientSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodUUID;
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
iss: z.ZodURL;
|
|
6
|
+
clientId: z.ZodString;
|
|
7
|
+
authUrl: z.ZodURL;
|
|
8
|
+
tokenUrl: z.ZodURL;
|
|
9
|
+
jwksUrl: z.ZodURL;
|
|
10
|
+
deployments: z.ZodArray<z.ZodObject<{
|
|
11
|
+
id: z.ZodUUID;
|
|
12
|
+
deploymentId: z.ZodString;
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
description: z.ZodOptional<z.ZodString>;
|
|
15
|
+
}, z.core.$strip>>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export declare const AddClientSchema: z.ZodObject<{
|
|
18
|
+
name: z.ZodString;
|
|
19
|
+
iss: z.ZodURL;
|
|
20
|
+
clientId: z.ZodString;
|
|
21
|
+
authUrl: z.ZodURL;
|
|
22
|
+
tokenUrl: z.ZodURL;
|
|
23
|
+
jwksUrl: z.ZodURL;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
export declare const UpdateClientSchema: z.ZodObject<{
|
|
26
|
+
name: z.ZodString;
|
|
27
|
+
iss: z.ZodURL;
|
|
28
|
+
clientId: z.ZodString;
|
|
29
|
+
authUrl: z.ZodURL;
|
|
30
|
+
tokenUrl: z.ZodURL;
|
|
31
|
+
jwksUrl: z.ZodURL;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
//# sourceMappingURL=client.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/client.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;iBASvB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;iBAAqD,CAAC;AAClF,eAAO,MAAM,kBAAkB;;;;;;;iBAAqD,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DeploymentSchema } from './deployment.schema';
|
|
3
|
+
export const ClientSchema = z.object({
|
|
4
|
+
id: z.uuid().describe('Internal stable UUID for the client'),
|
|
5
|
+
name: z.string().min(1).describe('human-readable name for the platform'),
|
|
6
|
+
iss: z.url().describe('Platform issuer (unique identifier)'),
|
|
7
|
+
clientId: z.string().min(1).describe("Your app's client ID on this platform"),
|
|
8
|
+
authUrl: z.url().describe("Platform's auth endpoint"),
|
|
9
|
+
tokenUrl: z.url().describe("Platform's token endpoint"),
|
|
10
|
+
jwksUrl: z.url().describe("Platform's JWKS endpoint"),
|
|
11
|
+
deployments: z.array(DeploymentSchema),
|
|
12
|
+
});
|
|
13
|
+
export const AddClientSchema = ClientSchema.omit({ id: true, deployments: true });
|
|
14
|
+
export const UpdateClientSchema = ClientSchema.omit({ id: true, deployments: true });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/common.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AAEH,eAAO,MAAM,eAAe,aAA6C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const DeploymentSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodUUID;
|
|
4
|
+
deploymentId: z.ZodString;
|
|
5
|
+
name: z.ZodOptional<z.ZodString>;
|
|
6
|
+
description: z.ZodOptional<z.ZodString>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
//# sourceMappingURL=deployment.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/deployment.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,gBAAgB;;;;;iBAS3B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const DeploymentSchema = z.object({
|
|
3
|
+
id: z.uuid().describe('Internal stable UUID for this deployment configuration'),
|
|
4
|
+
deploymentId: z.string().min(1).describe('LMS-provided deployment identifier'),
|
|
5
|
+
name: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.optional()
|
|
9
|
+
.describe('Optional human-readable name for the deployment'),
|
|
10
|
+
description: z.string().optional().describe('Optional description of the deployment'),
|
|
11
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SessionIdSchema } from './common.schema.js';
|
|
2
|
+
export { LTI13JwtPayloadSchema, type LTI13JwtPayload, } from './lti13/lti13JwtPayload.schema.js';
|
|
3
|
+
export { LTI13LaunchSchema, VerifyLaunchParamsSchema, } from './lti13/lti13Launch.schema.js';
|
|
4
|
+
export { HandleLoginParamsSchema, LTI13LoginSchema } from './lti13/lti13Login.schema.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { SessionIdSchema } from './common.schema.js';
|
|
2
|
+
export { LTI13JwtPayloadSchema, } from './lti13/lti13JwtPayload.schema.js';
|
|
3
|
+
export { LTI13LaunchSchema, VerifyLaunchParamsSchema, } from './lti13/lti13Launch.schema.js';
|
|
4
|
+
export { HandleLoginParamsSchema, LTI13LoginSchema } from './lti13/lti13Login.schema.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Schema for submitting grades via LTI Assignment and Grade Services (AGS).
|
|
4
|
+
* Validates score data according to LTI AGS v2.0 specification.
|
|
5
|
+
*
|
|
6
|
+
* @see https://www.imsglobal.org/spec/lti-ags/v2p0/#score-publish-service
|
|
7
|
+
*/
|
|
8
|
+
export declare const ScoreSubmissionSchema: z.ZodObject<{
|
|
9
|
+
scoreGiven: z.ZodNumber;
|
|
10
|
+
scoreMaximum: z.ZodNumber;
|
|
11
|
+
comment: z.ZodOptional<z.ZodString>;
|
|
12
|
+
userId: z.ZodOptional<z.ZodString>;
|
|
13
|
+
timestamp: z.ZodOptional<z.ZodISODateTime>;
|
|
14
|
+
activityProgress: z.ZodDefault<z.ZodEnum<{
|
|
15
|
+
Initialized: "Initialized";
|
|
16
|
+
Started: "Started";
|
|
17
|
+
InProgress: "InProgress";
|
|
18
|
+
Submitted: "Submitted";
|
|
19
|
+
Completed: "Completed";
|
|
20
|
+
}>>;
|
|
21
|
+
gradingProgress: z.ZodDefault<z.ZodEnum<{
|
|
22
|
+
NotReady: "NotReady";
|
|
23
|
+
Failed: "Failed";
|
|
24
|
+
Pending: "Pending";
|
|
25
|
+
PendingManual: "PendingManual";
|
|
26
|
+
FullyGraded: "FullyGraded";
|
|
27
|
+
}>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
/**
|
|
30
|
+
* Type representing a validated score submission for LTI AGS.
|
|
31
|
+
* Contains grade data and metadata to be sent to the platform.
|
|
32
|
+
*/
|
|
33
|
+
export type ScoreSubmission = z.infer<typeof ScoreSubmissionSchema>;
|
|
34
|
+
//# sourceMappingURL=scoreSubmission.schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoreSubmission.schema.d.ts","sourceRoot":"","sources":["../../../../src/schemas/lti13/ags/scoreSubmission.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;iBAuChC,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|