dms-middleware-auth 1.1.2 → 1.1.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.
@@ -21,6 +21,7 @@ export declare class AuthMiddleware implements NestMiddleware, OnModuleInit {
21
21
  private static shutdownInitiated;
22
22
  private static readonly licenceExpiredMessage;
23
23
  private static readonly CLOCK_TOLERANCE_MS;
24
+ private static readonly MAX_INT32;
24
25
  private readonly logger;
25
26
  constructor(cacheManager: Cache, options: AuthMiddlewareOptions);
26
27
  onModuleInit(): Promise<void>;
@@ -90,7 +90,9 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
90
90
  if (!clientToken) {
91
91
  clientToken = await this.clientLogin();
92
92
  const decodedToken = jwt.decode(clientToken);
93
- const ttl = (decodedToken.exp - Math.floor(Date.now() / 1000)) * 1000;
93
+ const ttlRaw = (decodedToken.exp - Math.floor(Date.now() / 1000)) * 1000;
94
+ const ttl = Math.min(Math.max(0, ttlRaw), AuthMiddleware_1.MAX_INT32);
95
+ console.error(`[AuthMiddleware] Client Token TTL: ${ttl}ms (Capped from ${ttlRaw})`);
94
96
  await this.cacheManager.set('client_access_token', clientToken, ttl);
95
97
  }
96
98
  // Cache client role attributes
@@ -232,10 +234,10 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
232
234
  try {
233
235
  const token = await this.verifyJwt(lic_data, publicKey);
234
236
  const licEndMs = this.normalizeEpochMs(token?.lic_end);
235
- // Reject when licence end (epoch) is missing or already in the past (with tolerance).
236
237
  const now = this.getUtcNowMs();
238
+ // Reject when licence end (epoch) is missing or already in the past (with tolerance).
237
239
  if (!licEndMs || (licEndMs + AuthMiddleware_1.CLOCK_TOLERANCE_MS) <= now) {
238
- this.logger.error(`Licence expired. Expiry: ${new Date(licEndMs || 0).toISOString()}, Server Time: ${new Date(now).toISOString()}`);
240
+ console.error(`[AUTH-ERROR] Licence expired. Expiry: ${new Date(licEndMs || 0).toISOString()}, Now: ${new Date(now).toISOString()}`);
239
241
  this.markLicenceExpired();
240
242
  return { status: false };
241
243
  }
@@ -243,7 +245,9 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
243
245
  AuthMiddleware_1.licenceValidatedUntilMs = licEndMs;
244
246
  this.scheduleLicenceShutdown(licEndMs);
245
247
  if (updateCache) {
246
- const ttl = (licEndMs - this.getUtcNowMs()) > 0 ? (licEndMs - this.getUtcNowMs()) : null;
248
+ const calculatedTtl = licEndMs - now;
249
+ const ttl = Math.min(Math.max(0, calculatedTtl), AuthMiddleware_1.MAX_INT32);
250
+ console.error(`[AuthMiddleware] Licence Cache TTL: ${ttl}ms (Capped from ${calculatedTtl})`);
247
251
  await this.cacheManager.set('client_Licence_token', lic_data, ttl);
248
252
  }
249
253
  return {
@@ -251,7 +255,13 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
251
255
  };
252
256
  }
253
257
  catch (error) {
254
- this.markLicenceExpired();
258
+ if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
259
+ this.logger.error(`Invalid Licence Token: ${error.message}`);
260
+ this.markLicenceExpired();
261
+ }
262
+ else {
263
+ this.logger.warn(`Transient error during licence validation: ${error.message}`);
264
+ }
255
265
  return { status: false };
256
266
  }
257
267
  }
@@ -267,25 +277,21 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
267
277
  const now = this.getUtcNowMs();
268
278
  const delay = (licEndMs + AuthMiddleware_1.CLOCK_TOLERANCE_MS) - now;
269
279
  if (delay <= 0) {
270
- this.logger.warn('Licence expired immediately (including tolerance).');
280
+ console.error('[AUTH-WARN] Licence expired immediately (including tolerance).');
271
281
  this.markLicenceExpired();
272
282
  return;
273
283
  }
274
284
  if (AuthMiddleware_1.licenceExpiryTimer) {
275
285
  clearTimeout(AuthMiddleware_1.licenceExpiryTimer);
276
286
  }
277
- // setTimeout uses a 32-bit signed integer. Max value is 2147483647 ms (approx 24.8 days).
278
- // If the delay is larger than this, setTimeout will fire immediately (overflow).
279
- // so we cap the delay and re-check when it fires.
280
- const MAX_TIMEOUT_VAL = 2147483647;
281
- const safeDelay = Math.min(delay, MAX_TIMEOUT_VAL);
287
+ const safeDelay = Math.min(delay, AuthMiddleware_1.MAX_INT32);
288
+ console.error(`[AuthMiddleware] Expiry timer set: ${safeDelay}ms (Full delay: ${delay}ms)`);
282
289
  AuthMiddleware_1.licenceExpiryTimer = setTimeout(() => {
283
290
  const remaining = (licEndMs + AuthMiddleware_1.CLOCK_TOLERANCE_MS) - this.getUtcNowMs();
284
291
  if (remaining <= 0) {
285
292
  this.markLicenceExpired();
286
293
  }
287
294
  else {
288
- // Reschedule if we still have time left
289
295
  this.scheduleLicenceShutdown(licEndMs);
290
296
  }
291
297
  }, safeDelay);
@@ -295,6 +301,7 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
295
301
  return;
296
302
  }
297
303
  AuthMiddleware_1.shutdownInitiated = true;
304
+ console.error(`[AUTH-DEBUG] setTimeout(stopServer) delay: 0`);
298
305
  setTimeout(() => {
299
306
  try {
300
307
  process.kill(process.pid, 'SIGTERM');
@@ -303,6 +310,7 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
303
310
  process.exit(1);
304
311
  }
305
312
  if (!AuthMiddleware_1.shutdownTimer) {
313
+ console.error(`[AUTH-DEBUG] setTimeout(shutdownTimer) delay: 5000`);
306
314
  AuthMiddleware_1.shutdownTimer = setTimeout(() => {
307
315
  process.exit(1);
308
316
  }, 5000);
@@ -349,6 +357,7 @@ AuthMiddleware.licenceValidationPromise = null;
349
357
  AuthMiddleware.shutdownInitiated = false;
350
358
  AuthMiddleware.licenceExpiredMessage = "server Licence is expired!. please renew";
351
359
  AuthMiddleware.CLOCK_TOLERANCE_MS = 60000; // 1 minute grace period
360
+ AuthMiddleware.MAX_INT32 = 2147483647;
352
361
  exports.AuthMiddleware = AuthMiddleware = AuthMiddleware_1 = __decorate([
353
362
  (0, common_1.Injectable)(),
354
363
  __param(0, (0, common_1.Inject)(cache_manager_1.CACHE_MANAGER)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dms-middleware-auth",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Reusable middleware for authentication and authorization in NestJS applications.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -25,8 +25,9 @@
25
25
  "@types/express": "^5.0.0",
26
26
  "@types/jest": "^29.5.14",
27
27
  "@types/jsonwebtoken": "^9.0.7",
28
+ "@types/node-forge": "^1.3.14",
28
29
  "jest": "^29.7.0",
29
30
  "ts-jest": "^29.3.4",
30
31
  "typescript": "^5.7.2"
31
32
  }
32
- }
33
+ }
@@ -26,6 +26,7 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
26
26
  private static shutdownInitiated = false;
27
27
  private static readonly licenceExpiredMessage = "server Licence is expired!. please renew";
28
28
  private static readonly CLOCK_TOLERANCE_MS = 60000; // 1 minute grace period
29
+ private static readonly MAX_INT32 = 2147483647;
29
30
  private readonly logger = new Logger('AuthMiddleware');
30
31
 
31
32
  constructor(
@@ -72,7 +73,9 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
72
73
  if (!clientToken) {
73
74
  clientToken = await this.clientLogin();
74
75
  const decodedToken: any = jwt.decode(clientToken);
75
- const ttl = (decodedToken.exp - Math.floor(Date.now() / 1000)) * 1000;
76
+ const ttlRaw = (decodedToken.exp - Math.floor(Date.now() / 1000)) * 1000;
77
+ const ttl = Math.min(Math.max(0, ttlRaw), AuthMiddleware.MAX_INT32);
78
+ console.error(`[AuthMiddleware] Client Token TTL: ${ttl}ms (Capped from ${ttlRaw})`);
76
79
  await this.cacheManager.set('client_access_token', clientToken, ttl);
77
80
  }
78
81
 
@@ -229,11 +232,11 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
229
232
  try {
230
233
  const token: any = await this.verifyJwt(lic_data, publicKey);
231
234
  const licEndMs = this.normalizeEpochMs(token?.lic_end);
235
+ const now = this.getUtcNowMs();
232
236
 
233
237
  // Reject when licence end (epoch) is missing or already in the past (with tolerance).
234
- const now = this.getUtcNowMs();
235
238
  if (!licEndMs || (licEndMs + AuthMiddleware.CLOCK_TOLERANCE_MS) <= now) {
236
- this.logger.error(`Licence expired. Expiry: ${new Date(licEndMs || 0).toISOString()}, Server Time: ${new Date(now).toISOString()}`);
239
+ console.error(`[AUTH-ERROR] Licence expired. Expiry: ${new Date(licEndMs || 0).toISOString()}, Now: ${new Date(now).toISOString()}`);
237
240
  this.markLicenceExpired();
238
241
  return { status: false };
239
242
  }
@@ -243,16 +246,22 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
243
246
  this.scheduleLicenceShutdown(licEndMs);
244
247
 
245
248
  if (updateCache) {
246
- const ttl: any = (licEndMs - this.getUtcNowMs()) > 0 ? (licEndMs - this.getUtcNowMs()) : null
249
+ const calculatedTtl = licEndMs - now;
250
+ const ttl = Math.min(Math.max(0, calculatedTtl), AuthMiddleware.MAX_INT32);
251
+ console.error(`[AuthMiddleware] Licence Cache TTL: ${ttl}ms (Capped from ${calculatedTtl})`);
247
252
  await this.cacheManager.set('client_Licence_token', lic_data, ttl);
248
253
  }
249
254
 
250
255
  return {
251
256
  status: true,
252
-
253
257
  };
254
258
  } catch (error: any) {
255
- this.markLicenceExpired();
259
+ if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
260
+ this.logger.error(`Invalid Licence Token: ${error.message}`);
261
+ this.markLicenceExpired();
262
+ } else {
263
+ this.logger.warn(`Transient error during licence validation: ${error.message}`);
264
+ }
256
265
  return { status: false };
257
266
  }
258
267
  }
@@ -271,7 +280,7 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
271
280
  const delay = (licEndMs + AuthMiddleware.CLOCK_TOLERANCE_MS) - now;
272
281
 
273
282
  if (delay <= 0) {
274
- this.logger.warn('Licence expired immediately (including tolerance).');
283
+ console.error('[AUTH-WARN] Licence expired immediately (including tolerance).');
275
284
  this.markLicenceExpired();
276
285
  return;
277
286
  }
@@ -280,18 +289,14 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
280
289
  clearTimeout(AuthMiddleware.licenceExpiryTimer);
281
290
  }
282
291
 
283
- // setTimeout uses a 32-bit signed integer. Max value is 2147483647 ms (approx 24.8 days).
284
- // If the delay is larger than this, setTimeout will fire immediately (overflow).
285
- // so we cap the delay and re-check when it fires.
286
- const MAX_TIMEOUT_VAL = 2147483647;
287
- const safeDelay = Math.min(delay, MAX_TIMEOUT_VAL);
292
+ const safeDelay = Math.min(delay, AuthMiddleware.MAX_INT32);
293
+ console.error(`[AuthMiddleware] Expiry timer set: ${safeDelay}ms (Full delay: ${delay}ms)`);
288
294
 
289
295
  AuthMiddleware.licenceExpiryTimer = setTimeout(() => {
290
296
  const remaining = (licEndMs + AuthMiddleware.CLOCK_TOLERANCE_MS) - this.getUtcNowMs();
291
297
  if (remaining <= 0) {
292
298
  this.markLicenceExpired();
293
299
  } else {
294
- // Reschedule if we still have time left
295
300
  this.scheduleLicenceShutdown(licEndMs);
296
301
  }
297
302
  }, safeDelay);
@@ -302,6 +307,7 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
302
307
  return;
303
308
  }
304
309
  AuthMiddleware.shutdownInitiated = true;
310
+ console.error(`[AUTH-DEBUG] setTimeout(stopServer) delay: 0`);
305
311
  setTimeout(() => {
306
312
  try {
307
313
  process.kill(process.pid, 'SIGTERM');
@@ -309,6 +315,7 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
309
315
  process.exit(1);
310
316
  }
311
317
  if (!AuthMiddleware.shutdownTimer) {
318
+ console.error(`[AUTH-DEBUG] setTimeout(shutdownTimer) delay: 5000`);
312
319
  AuthMiddleware.shutdownTimer = setTimeout(() => {
313
320
  process.exit(1);
314
321
  }, 5000);
@@ -0,0 +1,76 @@
1
+
2
+ import { AuthMiddleware } from './src/auth.middleware';
3
+ import * as jwt from 'jsonwebtoken';
4
+ import * as crypto from 'crypto';
5
+
6
+ async function runTest() {
7
+ console.log("--- Starting Thorough Test for Date 1774981740000 ---");
8
+
9
+ // 1. Setup Mocks
10
+ const mockCache = {
11
+ get: async (key: string) => undefined,
12
+ set: async (key: string, val: any, ttl: any) => {
13
+ console.log(`[MockCache] set key=${key}, ttl=${ttl}`);
14
+ },
15
+ } as any;
16
+
17
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
18
+ modulusLength: 2048,
19
+ });
20
+
21
+ let publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' }) as string;
22
+ // Strip headers as AuthMiddleware adds them
23
+ const publicKeyBody = publicKeyPem
24
+ .replace('-----BEGIN PUBLIC KEY-----\n', '')
25
+ .replace('\n-----END PUBLIC KEY-----\n', '');
26
+
27
+ const mockOptions = {
28
+ publicKey: publicKeyBody,
29
+ keycloakUrl: 'http://localhost:8080',
30
+ realm: 'test-realm',
31
+ clientId: 'test-client',
32
+ clientSecret: 'secret',
33
+ clientUuid: 'uuid',
34
+ bypassURL: '/health',
35
+ };
36
+
37
+ const authMiddleware = new AuthMiddleware(mockCache, mockOptions);
38
+
39
+ // 2. Create Token with specific date
40
+ const licEnd = 1774981740000;
41
+ const payload = {
42
+ lic_end: licEnd
43
+ };
44
+
45
+ const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' }) as string;
46
+ const token = jwt.sign(payload, privateKeyPem, { algorithm: 'RS256', noTimestamp: true });
47
+
48
+ console.log(`Test Token: ${token}`);
49
+ console.log(`Current Time (Date.now()): ${Date.now()}`);
50
+ console.log(`User's Date to test: ${licEnd}`);
51
+
52
+ // 3. Run Validation
53
+ console.log("\n--- Calling validateLicence ---");
54
+ // We can access private methods using any cast for testing
55
+ const result = await (authMiddleware as any).validateLicence(token, publicKeyBody, true);
56
+
57
+ console.log(`\nResult Status: ${result.status}`);
58
+
59
+ if (result.status) {
60
+ console.log("SUCCESS: Middleware accepted the future date.");
61
+ } else {
62
+ console.error("FAILURE: Middleware rejected the future date.");
63
+ }
64
+
65
+ // 4. Check if expired flag was set
66
+ const isExpired = (AuthMiddleware as any).licenceExpired;
67
+ console.log(`AuthMiddleware.licenceExpired: ${isExpired}`);
68
+
69
+ // 5. Cleanup Timer
70
+ if ((AuthMiddleware as any).licenceExpiryTimer) {
71
+ console.log("Clearing expiry timer...");
72
+ clearTimeout((AuthMiddleware as any).licenceExpiryTimer);
73
+ }
74
+ }
75
+
76
+ runTest().catch(console.error);
@@ -0,0 +1,31 @@
1
+
2
+ import { caching } from 'cache-manager';
3
+
4
+ async function testTTL() {
5
+ console.log("--- Testing Cache TTL Overflow ---");
6
+ const cache = await caching('memory');
7
+
8
+ const largeTTL = 4351255821; // ~50 days in ms
9
+ console.log(`Setting key with TTL: ${largeTTL}ms`);
10
+
11
+ await cache.set('test_key', 'test_value', largeTTL);
12
+
13
+ const val = await cache.get('test_key');
14
+ console.log(`Immediate get: ${val}`);
15
+
16
+ if (!val) {
17
+ console.error("FAIL: Value expired immediately!");
18
+ } else {
19
+ console.log("Value still present. Waiting 2s to check again...");
20
+ await new Promise(resolve => setTimeout(resolve, 2000));
21
+ const val2 = await cache.get('test_key');
22
+ console.log(`After 2s get: ${val2}`);
23
+ if (!val2) {
24
+ console.error("FAIL: Value expired after 2s (likely overflow)!");
25
+ } else {
26
+ console.log("SUCCESS: Cache handles large TTL.");
27
+ }
28
+ }
29
+ }
30
+
31
+ testTTL().catch(console.error);