@lenne.tech/nest-server 10.2.3 → 10.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "10.2.3",
3
+ "version": "10.2.4",
4
4
  "description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
5
5
  "keywords": [
6
6
  "node",
package/src/config.env.ts CHANGED
@@ -86,6 +86,7 @@ const config: { [env: string]: IServerOptions } = {
86
86
  expiresIn: '7d',
87
87
  },
88
88
  },
89
+ sameTokenIdPeriod: 2000,
89
90
  },
90
91
  loadLocalConfig: true,
91
92
  logExceptions: true,
@@ -176,6 +177,7 @@ const config: { [env: string]: IServerOptions } = {
176
177
  expiresIn: '7d',
177
178
  },
178
179
  },
180
+ sameTokenIdPeriod: 2000,
179
181
  },
180
182
  loadLocalConfig: false,
181
183
  logExceptions: true,
@@ -266,6 +268,7 @@ const config: { [env: string]: IServerOptions } = {
266
268
  expiresIn: '7d',
267
269
  },
268
270
  },
271
+ sameTokenIdPeriod: 2000,
269
272
  },
270
273
  loadLocalConfig: false,
271
274
  logExceptions: true,
@@ -315,6 +315,13 @@ export interface IServerOptions {
315
315
  */
316
316
  renewal?: boolean;
317
317
  } & IJwt;
318
+
319
+ /**
320
+ * Time period in milliseconds
321
+ * in which the same token ID is used so that all parallel token refresh requests of a device can be generated.
322
+ * default: 0 (every token includes a new token ID, all parallel token refresh requests must be prevented by the client or processed accordingly)
323
+ */
324
+ sameTokenIdPeriod?: number;
318
325
  } & IJwt &
319
326
  JwtModuleOptions;
320
327
 
@@ -74,7 +74,7 @@ export class ConfigService {
74
74
  // Init subject handling
75
75
  if (!isInitialized) {
76
76
  ConfigService._configSubject$.subscribe((config) => {
77
- ConfigService._frozenConfigSubject$.next(deepFreeze(config));
77
+ ConfigService._frozenConfigSubject$.next(deepFreeze(cloneDeep(config)));
78
78
  });
79
79
  }
80
80
 
@@ -370,7 +370,7 @@ export class ConfigService {
370
370
  * Set config property in ConfigService
371
371
  */
372
372
  setProperty(key: string, value: any, options?: { warn?: boolean }) {
373
- return ConfigService.setProperty(key, options);
373
+ return ConfigService.setProperty(key, value, options);
374
374
  }
375
375
 
376
376
  /**
@@ -23,4 +23,10 @@ export interface ICoreAuthUser {
23
23
  * Refresh tokens for different devices
24
24
  */
25
25
  refreshTokens?: Record<string, CoreTokenData>;
26
+
27
+ /**
28
+ * Temporary tokens for parallel requests during the token refresh process
29
+ * See sameTokenIdPeriod in configuration
30
+ */
31
+ tempTokens?: Record<string, { createdAt: number; deviceId: string; tokenId: string }>;
26
32
  }
@@ -204,11 +204,26 @@ export class CoreAuthService {
204
204
  * Get JWT and refresh token
205
205
  */
206
206
  protected async createTokens(userId: string, data?: { [key: string]: any; deviceId?: string }) {
207
- const payload: { [key: string]: any; id: string; deviceId: string } = {
207
+
208
+ // Initializations
209
+ const sameTokenIdPeriod: number = this.configService.getFastButReadOnly('jwt.sameTokenIdPeriod', 0);
210
+ const deviceId = data?.deviceId || randomUUID();
211
+
212
+ // Use last token ID or a new one
213
+ let tokenId: string = randomUUID();
214
+ if (sameTokenIdPeriod) {
215
+ const user: ICoreAuthUser = await this.userService.get(userId, { force: true });
216
+ const tempToken = user?.tempTokens?.[deviceId];
217
+ if (tempToken && tempToken.tokenId && tempToken.createdAt >= new Date().getTime() - sameTokenIdPeriod) {
218
+ tokenId = tempToken.tokenId;
219
+ }
220
+ }
221
+
222
+ const payload: { [key: string]: any; id: string; deviceId: string; tokenId: string } = {
208
223
  ...data,
209
224
  id: userId,
210
- deviceId: data?.deviceId || randomUUID(),
211
- tokenId: randomUUID(),
225
+ deviceId,
226
+ tokenId,
212
227
  };
213
228
  const [token, refreshToken] = await Promise.all([
214
229
  this.jwtService.signAsync(payload, {
@@ -253,6 +268,9 @@ export class CoreAuthService {
253
268
  if (!user.refreshTokens) {
254
269
  user.refreshTokens = {};
255
270
  }
271
+ if (!user.tempTokens) {
272
+ user.tempTokens = {};
273
+ }
256
274
  if (deviceId) {
257
275
  const oldData = user.refreshTokens[deviceId] || {};
258
276
  data = Object.assign(oldData, data);
@@ -267,7 +285,11 @@ export class CoreAuthService {
267
285
  deviceId = payload.deviceId;
268
286
  }
269
287
  user.refreshTokens[deviceId] = { ...data, deviceId, tokenId: payload.tokenId };
270
- await this.userService.update(getStringIds(user), { refreshTokens: user.refreshTokens }, { force: true });
288
+ user.tempTokens[deviceId] = { createdAt: new Date().getTime(), deviceId, tokenId: payload.tokenId };
289
+ await this.userService.update(getStringIds(user), {
290
+ refreshTokens: user.refreshTokens,
291
+ tempTokens: user.tempTokens,
292
+ }, { force: true });
271
293
 
272
294
  // Return new token
273
295
  return newRefreshToken;
@@ -79,6 +79,14 @@ export abstract class CoreUserModel extends CorePersistenceModel {
79
79
  @Prop(raw({}))
80
80
  refreshTokens: Record<string, CoreTokenData> = undefined;
81
81
 
82
+ /**
83
+ * Temporary token for parallel requests during the token refresh process
84
+ * See sameTokenIdPeriod in configuration
85
+ */
86
+ @IsOptional()
87
+ @Prop(raw({}))
88
+ tempTokens: Record<string, { createdAt: number; deviceId: string; tokenId: string }> = undefined;
89
+
82
90
  /**
83
91
  * Verification token of the user
84
92
  */
@@ -606,4 +606,12 @@ export class TestHelper {
606
606
  // Return subscribed messages
607
607
  return messages;
608
608
  }
609
+
610
+ /**
611
+ * Convert JWT into to object
612
+ * @param token
613
+ */
614
+ parseJwt(token) {
615
+ return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
616
+ }
609
617
  }