@lti-tool/core 1.0.6 → 1.0.7

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
+ ## 1.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - b0a98e0: Resiliency -- retry launch JWT verification on JWKS kid miss -- @ottenhoff 🎉 #94
8
+
3
9
  ## 1.0.6
4
10
 
5
11
  ### Patch Changes
package/dist/ltiTool.d.ts CHANGED
@@ -89,6 +89,8 @@ export declare class LTITool {
89
89
  * @throws {Error} When verification fails for security reasons
90
90
  */
91
91
  verifyLaunch(idToken: string, state: string): Promise<LTI13JwtPayload>;
92
+ private getOrCreateJwks;
93
+ private verifyLaunchJwtWithCachedJwks;
92
94
  /**
93
95
  * Generates JSON Web Key Set (JWKS) containing the tool's public key for platform verification.
94
96
  *
@@ -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
@@ -133,12 +133,7 @@ export class LTITool {
133
133
  // 2. get the launchConfig so we can get the remote JWKS from our data store
134
134
  const launchConfig = await getValidLaunchConfig(this.config.storage, unverified.iss, unverified.aud, unverified['https://purl.imsglobal.org/spec/lti/claim/deployment_id']);
135
135
  // 3. Verify LMS JWT
136
- let jwks = this.jwksCache.get(launchConfig.jwksUrl);
137
- if (!jwks) {
138
- jwks = createRemoteJWKSet(new URL(launchConfig.jwksUrl));
139
- this.jwksCache.set(launchConfig.jwksUrl, jwks);
140
- }
141
- const { payload } = await jwtVerify(validatedParams.idToken, jwks);
136
+ const payload = await this.verifyLaunchJwtWithCachedJwks(validatedParams.idToken, launchConfig.jwksUrl);
142
137
  // 4. Verify our state JWT
143
138
  const { payload: stateData } = await jwtVerify(validatedParams.state, this.config.stateSecret);
144
139
  // 5. Parse and validate LMS JWT
@@ -162,6 +157,31 @@ export class LTITool {
162
157
  throw new Error(`[LTI] Launch verification failed: ${formatError(error)}`);
163
158
  }
164
159
  }
160
+ getOrCreateJwks(jwksUrl) {
161
+ let jwks = this.jwksCache.get(jwksUrl);
162
+ if (!jwks) {
163
+ jwks = createRemoteJWKSet(new URL(jwksUrl));
164
+ this.jwksCache.set(jwksUrl, jwks);
165
+ }
166
+ return jwks;
167
+ }
168
+ async verifyLaunchJwtWithCachedJwks(idToken, jwksUrl) {
169
+ const jwks = this.getOrCreateJwks(jwksUrl);
170
+ try {
171
+ const { payload } = await jwtVerify(idToken, jwks);
172
+ return payload;
173
+ }
174
+ catch (error) {
175
+ if (error.code !== 'ERR_JWKS_NO_MATCHING_KEY') {
176
+ throw error;
177
+ }
178
+ // Key rotation fallback: evict cached JWKS and retry verification once.
179
+ this.jwksCache.delete(jwksUrl);
180
+ const refreshedJwks = this.getOrCreateJwks(jwksUrl);
181
+ const { payload } = await jwtVerify(idToken, refreshedJwks);
182
+ return payload;
183
+ }
184
+ }
165
185
  /**
166
186
  * Generates JSON Web Key Set (JWKS) containing the tool's public key for platform verification.
167
187
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lti-tool/core",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "LTI 1.3 implementation for Node.js",
5
5
  "keywords": [
6
6
  "lti",
package/src/ltiTool.ts CHANGED
@@ -208,12 +208,10 @@ export class LTITool {
208
208
  );
209
209
 
210
210
  // 3. Verify LMS JWT
211
- let jwks = this.jwksCache.get(launchConfig.jwksUrl);
212
- if (!jwks) {
213
- jwks = createRemoteJWKSet(new URL(launchConfig.jwksUrl));
214
- this.jwksCache.set(launchConfig.jwksUrl, jwks);
215
- }
216
- const { payload } = await jwtVerify(validatedParams.idToken, jwks);
211
+ const payload = await this.verifyLaunchJwtWithCachedJwks(
212
+ validatedParams.idToken,
213
+ launchConfig.jwksUrl,
214
+ );
217
215
 
218
216
  // 4. Verify our state JWT
219
217
  const { payload: stateData } = await jwtVerify(
@@ -248,6 +246,37 @@ export class LTITool {
248
246
  }
249
247
  }
250
248
 
249
+ private getOrCreateJwks(jwksUrl: string): ReturnType<typeof createRemoteJWKSet> {
250
+ let jwks = this.jwksCache.get(jwksUrl);
251
+ if (!jwks) {
252
+ jwks = createRemoteJWKSet(new URL(jwksUrl));
253
+ this.jwksCache.set(jwksUrl, jwks);
254
+ }
255
+ return jwks;
256
+ }
257
+
258
+ private async verifyLaunchJwtWithCachedJwks(
259
+ idToken: string,
260
+ jwksUrl: string,
261
+ ): Promise<LTI13JwtPayload> {
262
+ const jwks = this.getOrCreateJwks(jwksUrl);
263
+
264
+ try {
265
+ const { payload } = await jwtVerify(idToken, jwks);
266
+ return payload as LTI13JwtPayload;
267
+ } catch (error) {
268
+ if ((error as { code?: string }).code !== 'ERR_JWKS_NO_MATCHING_KEY') {
269
+ throw error;
270
+ }
271
+
272
+ // Key rotation fallback: evict cached JWKS and retry verification once.
273
+ this.jwksCache.delete(jwksUrl);
274
+ const refreshedJwks = this.getOrCreateJwks(jwksUrl);
275
+ const { payload } = await jwtVerify(idToken, refreshedJwks);
276
+ return payload as LTI13JwtPayload;
277
+ }
278
+ }
279
+
251
280
  /**
252
281
  * Generates JSON Web Key Set (JWKS) containing the tool's public key for platform verification.
253
282
  *