dms-middleware-auth 1.1.2 → 1.1.3
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/dist/auth.middleware.js +12 -3
- package/package.json +3 -2
- package/src/auth.middleware.ts +11 -4
- package/test_specific_date.ts +76 -0
- package/test_ttl_overflow.ts +31 -0
package/dist/auth.middleware.js
CHANGED
|
@@ -232,8 +232,8 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
|
|
|
232
232
|
try {
|
|
233
233
|
const token = await this.verifyJwt(lic_data, publicKey);
|
|
234
234
|
const licEndMs = this.normalizeEpochMs(token?.lic_end);
|
|
235
|
-
// Reject when licence end (epoch) is missing or already in the past (with tolerance).
|
|
236
235
|
const now = this.getUtcNowMs();
|
|
236
|
+
// Reject when licence end (epoch) is missing or already in the past (with tolerance).
|
|
237
237
|
if (!licEndMs || (licEndMs + AuthMiddleware_1.CLOCK_TOLERANCE_MS) <= now) {
|
|
238
238
|
this.logger.error(`Licence expired. Expiry: ${new Date(licEndMs || 0).toISOString()}, Server Time: ${new Date(now).toISOString()}`);
|
|
239
239
|
this.markLicenceExpired();
|
|
@@ -243,7 +243,10 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
|
|
|
243
243
|
AuthMiddleware_1.licenceValidatedUntilMs = licEndMs;
|
|
244
244
|
this.scheduleLicenceShutdown(licEndMs);
|
|
245
245
|
if (updateCache) {
|
|
246
|
-
|
|
246
|
+
// Cap TTL to avoid timeout overflow in cache managers (32-bit signed int max is ~24.8 days)
|
|
247
|
+
const MAX_TTL = 2147483647;
|
|
248
|
+
const calculatedTtl = licEndMs - now;
|
|
249
|
+
const ttl = Math.min(Math.max(0, calculatedTtl), MAX_TTL);
|
|
247
250
|
await this.cacheManager.set('client_Licence_token', lic_data, ttl);
|
|
248
251
|
}
|
|
249
252
|
return {
|
|
@@ -251,7 +254,13 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
|
|
|
251
254
|
};
|
|
252
255
|
}
|
|
253
256
|
catch (error) {
|
|
254
|
-
|
|
257
|
+
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
|
|
258
|
+
this.logger.error(`Invalid Licence Token: ${error.message}`);
|
|
259
|
+
this.markLicenceExpired();
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
this.logger.warn(`Transient error during licence validation: ${error.message}`);
|
|
263
|
+
}
|
|
255
264
|
return { status: false };
|
|
256
265
|
}
|
|
257
266
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dms-middleware-auth",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
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
|
+
}
|
package/src/auth.middleware.ts
CHANGED
|
@@ -229,9 +229,9 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
|
|
|
229
229
|
try {
|
|
230
230
|
const token: any = await this.verifyJwt(lic_data, publicKey);
|
|
231
231
|
const licEndMs = this.normalizeEpochMs(token?.lic_end);
|
|
232
|
+
const now = this.getUtcNowMs();
|
|
232
233
|
|
|
233
234
|
// Reject when licence end (epoch) is missing or already in the past (with tolerance).
|
|
234
|
-
const now = this.getUtcNowMs();
|
|
235
235
|
if (!licEndMs || (licEndMs + AuthMiddleware.CLOCK_TOLERANCE_MS) <= now) {
|
|
236
236
|
this.logger.error(`Licence expired. Expiry: ${new Date(licEndMs || 0).toISOString()}, Server Time: ${new Date(now).toISOString()}`);
|
|
237
237
|
this.markLicenceExpired();
|
|
@@ -243,16 +243,23 @@ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
|
|
|
243
243
|
this.scheduleLicenceShutdown(licEndMs);
|
|
244
244
|
|
|
245
245
|
if (updateCache) {
|
|
246
|
-
|
|
246
|
+
// Cap TTL to avoid timeout overflow in cache managers (32-bit signed int max is ~24.8 days)
|
|
247
|
+
const MAX_TTL = 2147483647;
|
|
248
|
+
const calculatedTtl = licEndMs - now;
|
|
249
|
+
const ttl = Math.min(Math.max(0, calculatedTtl), MAX_TTL);
|
|
247
250
|
await this.cacheManager.set('client_Licence_token', lic_data, ttl);
|
|
248
251
|
}
|
|
249
252
|
|
|
250
253
|
return {
|
|
251
254
|
status: true,
|
|
252
|
-
|
|
253
255
|
};
|
|
254
256
|
} catch (error: any) {
|
|
255
|
-
|
|
257
|
+
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
|
|
258
|
+
this.logger.error(`Invalid Licence Token: ${error.message}`);
|
|
259
|
+
this.markLicenceExpired();
|
|
260
|
+
} else {
|
|
261
|
+
this.logger.warn(`Transient error during licence validation: ${error.message}`);
|
|
262
|
+
}
|
|
256
263
|
return { status: false };
|
|
257
264
|
}
|
|
258
265
|
}
|
|
@@ -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);
|