dms-middleware-auth 1.0.9 → 1.0.10

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.
@@ -1,4 +1,4 @@
1
- import { NestMiddleware } from '@nestjs/common';
1
+ import { NestMiddleware, OnModuleInit } from '@nestjs/common';
2
2
  import { Response, NextFunction } from 'express';
3
3
  import { Cache } from 'cache-manager';
4
4
  interface AuthMiddlewareOptions {
@@ -10,13 +10,18 @@ interface AuthMiddlewareOptions {
10
10
  clientUuid: string;
11
11
  bypassURL: string;
12
12
  }
13
- export declare class AuthMiddleware implements NestMiddleware {
13
+ export declare class AuthMiddleware implements NestMiddleware, OnModuleInit {
14
14
  private cacheManager;
15
15
  private readonly options;
16
16
  private static licenceExpired;
17
17
  private static shutdownTimer;
18
+ private static licenceExpiryTimer;
19
+ private static licenceValidatedUntilMs;
20
+ private static licenceValidationPromise;
21
+ private static shutdownInitiated;
18
22
  private static readonly licenceExpiredMessage;
19
23
  constructor(cacheManager: Cache, options: AuthMiddlewareOptions);
24
+ onModuleInit(): Promise<void>;
20
25
  use(req: any, res: Response, next: NextFunction): Promise<void | Response<any, Record<string, any>>>;
21
26
  private clientLogin;
22
27
  private getUserDetails;
@@ -25,6 +30,8 @@ export declare class AuthMiddleware implements NestMiddleware {
25
30
  private getLicencingDetails;
26
31
  private validateLicence;
27
32
  private markLicenceExpired;
33
+ private scheduleLicenceShutdown;
34
+ private stopServer;
28
35
  private normalizeEpochMs;
29
36
  private verifyJwt;
30
37
  private decodeAccessToken;
@@ -57,18 +57,23 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
57
57
  this.cacheManager = cacheManager;
58
58
  this.options = options;
59
59
  }
60
+ async onModuleInit() {
61
+ const { publicKey, realm } = this.options;
62
+ const isValid = await this.checkLicenceAndValidate(realm, publicKey);
63
+ if (!isValid) {
64
+ this.stopServer();
65
+ }
66
+ }
60
67
  async use(req, res, next) {
61
68
  const { publicKey, clientId, realm, bypassURL } = this.options;
62
69
  try {
63
70
  if (AuthMiddleware_1.licenceExpired) {
64
71
  return res.status(axios_1.HttpStatusCode.Forbidden).json({ message: AuthMiddleware_1.licenceExpiredMessage });
65
72
  }
66
- // fetching the licence and validating.
67
- const validate_lic = await this.checkLicenceAndValidate(realm, publicKey);
68
- if (!validate_lic) {
73
+ if (!AuthMiddleware_1.licenceValidatedUntilMs) {
69
74
  return res.status(axios_1.HttpStatusCode.Forbidden).json({ message: AuthMiddleware_1.licenceExpiredMessage });
70
75
  }
71
- else if (req.originalUrl == bypassURL && validate_lic) {
76
+ if (req.originalUrl == bypassURL) {
72
77
  return next();
73
78
  }
74
79
  const authHeader = req.headers['authorization'];
@@ -170,26 +175,30 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
170
175
  }
171
176
  async checkLicenceAndValidate(realm, publicKey) {
172
177
  try {
173
- // Reuse cached licence when possible; short-circuit if already marked expired.
174
178
  if (AuthMiddleware_1.licenceExpired) {
175
179
  return false;
176
180
  }
177
- const lic_token = await this.cacheManager.get('client_Licence_token');
178
- if (!lic_token) {
181
+ if (AuthMiddleware_1.licenceValidatedUntilMs && AuthMiddleware_1.licenceValidatedUntilMs > Date.now()) {
182
+ return true;
183
+ }
184
+ if (AuthMiddleware_1.licenceValidationPromise) {
185
+ return await AuthMiddleware_1.licenceValidationPromise;
186
+ }
187
+ AuthMiddleware_1.licenceValidationPromise = (async () => {
179
188
  const response = await this.getLicencingDetails(realm);
180
189
  if (response.code === axios_1.HttpStatusCode.InternalServerError) {
181
190
  return false;
182
191
  }
183
192
  else {
184
193
  const validate = await this.validateLicence(response.data, publicKey);
185
- if (validate.status && validate.ttl) {
186
- await this.cacheManager.set('client_Licence_token', response.data, validate.ttl);
187
- }
188
194
  return validate.status;
189
195
  }
196
+ })();
197
+ try {
198
+ return await AuthMiddleware_1.licenceValidationPromise;
190
199
  }
191
- else {
192
- return await this.validateLicence(lic_token, publicKey);
200
+ finally {
201
+ AuthMiddleware_1.licenceValidationPromise = null;
193
202
  }
194
203
  }
195
204
  catch (error) {
@@ -227,6 +236,7 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
227
236
  const licEndMs = this.normalizeEpochMs(token?.lic_end);
228
237
  // Reject when licence end (epoch) is missing or already in the past.
229
238
  if (!licEndMs) {
239
+ this.markLicenceExpired();
230
240
  return {
231
241
  status: false
232
242
  };
@@ -238,6 +248,8 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
238
248
  };
239
249
  }
240
250
  else {
251
+ AuthMiddleware_1.licenceValidatedUntilMs = licEndMs;
252
+ this.scheduleLicenceShutdown(licEndMs);
241
253
  return {
242
254
  status: true,
243
255
  ttl: (licEndMs - Date.now()) > 0 ? (licEndMs - Date.now()) : null
@@ -246,18 +258,51 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
246
258
  }
247
259
  catch (error) {
248
260
  this.markLicenceExpired();
249
- return false;
261
+ return {
262
+ status: false
263
+ };
250
264
  }
251
265
  }
252
266
  markLicenceExpired() {
253
267
  if (!AuthMiddleware_1.licenceExpired) {
254
268
  AuthMiddleware_1.licenceExpired = true;
255
269
  }
256
- if (!AuthMiddleware_1.shutdownTimer) {
257
- AuthMiddleware_1.shutdownTimer = setTimeout(() => {
258
- process.exit(1);
259
- }, 2 * 60 * 1000); // waiting two minutes after shutdowning server.
270
+ this.stopServer();
271
+ }
272
+ scheduleLicenceShutdown(licEndMs) {
273
+ if (!licEndMs) {
274
+ return;
275
+ }
276
+ const delay = licEndMs - Date.now();
277
+ if (delay <= 0) {
278
+ this.markLicenceExpired();
279
+ return;
260
280
  }
281
+ if (AuthMiddleware_1.licenceExpiryTimer) {
282
+ return;
283
+ }
284
+ AuthMiddleware_1.licenceExpiryTimer = setTimeout(() => {
285
+ this.markLicenceExpired();
286
+ }, delay);
287
+ }
288
+ stopServer() {
289
+ if (AuthMiddleware_1.shutdownInitiated) {
290
+ return;
291
+ }
292
+ AuthMiddleware_1.shutdownInitiated = true;
293
+ setTimeout(() => {
294
+ try {
295
+ process.kill(process.pid, 'SIGTERM');
296
+ }
297
+ catch {
298
+ process.exit(1);
299
+ }
300
+ if (!AuthMiddleware_1.shutdownTimer) {
301
+ AuthMiddleware_1.shutdownTimer = setTimeout(() => {
302
+ process.exit(1);
303
+ }, 5000);
304
+ }
305
+ }, 0);
261
306
  }
262
307
  normalizeEpochMs(epoch) {
263
308
  if (!epoch || Number.isNaN(epoch)) {
@@ -284,6 +329,10 @@ let AuthMiddleware = AuthMiddleware_1 = class AuthMiddleware {
284
329
  exports.AuthMiddleware = AuthMiddleware;
285
330
  AuthMiddleware.licenceExpired = false;
286
331
  AuthMiddleware.shutdownTimer = null;
332
+ AuthMiddleware.licenceExpiryTimer = null;
333
+ AuthMiddleware.licenceValidatedUntilMs = null;
334
+ AuthMiddleware.licenceValidationPromise = null;
335
+ AuthMiddleware.shutdownInitiated = false;
287
336
  AuthMiddleware.licenceExpiredMessage = "server Licence is expired!. please renew";
288
337
  exports.AuthMiddleware = AuthMiddleware = AuthMiddleware_1 = __decorate([
289
338
  (0, common_1.Injectable)(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dms-middleware-auth",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
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",
@@ -1,4 +1,4 @@
1
- import { Inject, Injectable, NestMiddleware } from '@nestjs/common';
1
+ import { Inject, Injectable, NestMiddleware, OnModuleInit } from '@nestjs/common';
2
2
  import { Response, NextFunction } from 'express';
3
3
  import * as jwt from 'jsonwebtoken';
4
4
  import axios, { HttpStatusCode } from 'axios';
@@ -16,17 +16,29 @@ interface AuthMiddlewareOptions {
16
16
  }
17
17
 
18
18
  @Injectable()
19
- export class AuthMiddleware implements NestMiddleware {
20
- private static licenceExpired = false;
21
- private static shutdownTimer: NodeJS.Timeout | null = null;
22
- private static readonly licenceExpiredMessage = "server Licence is expired!. please renew";
23
-
24
- constructor(
25
- @Inject(CACHE_MANAGER) private cacheManager: Cache,
26
- @Inject('AUTH_MIDDLEWARE_OPTIONS') private readonly options: AuthMiddlewareOptions
27
- ) {}
28
-
29
- async use(req: any, res: Response, next: NextFunction) {
19
+ export class AuthMiddleware implements NestMiddleware, OnModuleInit {
20
+ private static licenceExpired = false;
21
+ private static shutdownTimer: NodeJS.Timeout | null = null;
22
+ private static licenceExpiryTimer: NodeJS.Timeout | null = null;
23
+ private static licenceValidatedUntilMs: number | null = null;
24
+ private static licenceValidationPromise: Promise<boolean> | null = null;
25
+ private static shutdownInitiated = false;
26
+ private static readonly licenceExpiredMessage = "server Licence is expired!. please renew";
27
+
28
+ constructor(
29
+ @Inject(CACHE_MANAGER) private cacheManager: Cache,
30
+ @Inject('AUTH_MIDDLEWARE_OPTIONS') private readonly options: AuthMiddlewareOptions
31
+ ) {}
32
+
33
+ async onModuleInit() {
34
+ const { publicKey, realm } = this.options;
35
+ const isValid = await this.checkLicenceAndValidate(realm, publicKey);
36
+ if (!isValid) {
37
+ this.stopServer();
38
+ }
39
+ }
40
+
41
+ async use(req: any, res: Response, next: NextFunction) {
30
42
 
31
43
 
32
44
  const { publicKey, clientId, realm, bypassURL } = this.options;
@@ -36,14 +48,12 @@ export class AuthMiddleware implements NestMiddleware {
36
48
  return res.status(HttpStatusCode.Forbidden).json({ message: AuthMiddleware.licenceExpiredMessage });
37
49
  }
38
50
 
39
- // fetching the licence and validating.
40
- const validate_lic = await this.checkLicenceAndValidate(realm, publicKey);
41
- if (!validate_lic) {
42
- return res.status(HttpStatusCode.Forbidden).json({message: AuthMiddleware.licenceExpiredMessage});
43
- }
44
- else if (req.originalUrl == bypassURL && validate_lic ) {
45
- return next();
46
- }
51
+ if (!AuthMiddleware.licenceValidatedUntilMs) {
52
+ return res.status(HttpStatusCode.Forbidden).json({ message: AuthMiddleware.licenceExpiredMessage });
53
+ }
54
+ if (req.originalUrl == bypassURL) {
55
+ return next();
56
+ }
47
57
  const authHeader = req.headers['authorization'];
48
58
 
49
59
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
@@ -163,34 +173,36 @@ export class AuthMiddleware implements NestMiddleware {
163
173
  }
164
174
  }
165
175
 
166
- private async checkLicenceAndValidate(realm: string, publicKey:string) {
167
- try {
168
- // Reuse cached licence when possible; short-circuit if already marked expired.
169
- if (AuthMiddleware.licenceExpired) {
170
- return false;
171
- }
172
- const lic_token = await this.cacheManager.get( 'client_Licence_token' );
173
- if (!lic_token) {
174
- const response:any = await this.getLicencingDetails(realm);
175
- if (response.code === HttpStatusCode.InternalServerError) {
176
- return false
177
- }
178
- else{
179
- const validate:any = await this.validateLicence(response.data, publicKey);
180
- if (validate.status && validate.ttl) {
181
- await this.cacheManager.set('client_Licence_token', response.data, validate.ttl);
182
- }
183
-
184
- return validate.status;
185
- }
186
- }
187
- else {
188
- return await this.validateLicence(lic_token, publicKey);
189
- }
190
- }
191
- catch(error:any){
192
- return false;
193
-
176
+ private async checkLicenceAndValidate(realm: string, publicKey:string) {
177
+ try {
178
+ if (AuthMiddleware.licenceExpired) {
179
+ return false;
180
+ }
181
+ if (AuthMiddleware.licenceValidatedUntilMs && AuthMiddleware.licenceValidatedUntilMs > Date.now()) {
182
+ return true;
183
+ }
184
+ if (AuthMiddleware.licenceValidationPromise) {
185
+ return await AuthMiddleware.licenceValidationPromise;
186
+ }
187
+ AuthMiddleware.licenceValidationPromise = (async () => {
188
+ const response:any = await this.getLicencingDetails(realm);
189
+ if (response.code === HttpStatusCode.InternalServerError) {
190
+ return false
191
+ }
192
+ else{
193
+ const validate = await this.validateLicence(response.data, publicKey);
194
+ return validate.status;
195
+ }
196
+ })();
197
+ try {
198
+ return await AuthMiddleware.licenceValidationPromise;
199
+ } finally {
200
+ AuthMiddleware.licenceValidationPromise = null;
201
+ }
202
+ }
203
+ catch(error:any){
204
+ return false;
205
+
194
206
  }
195
207
  }
196
208
  private async getLicencingDetails(realm: string) {
@@ -222,45 +234,81 @@ export class AuthMiddleware implements NestMiddleware {
222
234
  };
223
235
  }
224
236
  }
225
- private async validateLicence(lic_data:any, publicKey:any){
226
- try{
227
- const token:any = await this.verifyJwt(lic_data, publicKey);
228
- const licEndMs = this.normalizeEpochMs(token?.lic_end);
229
- // Reject when licence end (epoch) is missing or already in the past.
230
- if (!licEndMs) {
231
- return{
232
- status: false
233
- }
234
- }
235
- if (licEndMs <= Date.now()) {
236
- this.markLicenceExpired();
237
- return {
238
- status: false
239
- }
240
- } else {
241
-
242
- return {
243
- status: true,
244
- ttl: (licEndMs - Date.now()) > 0 ? (licEndMs - Date.now()) : null
245
- }
246
- }
247
- }
248
- catch(error:any){
249
- this.markLicenceExpired();
250
- return false;
251
- }
252
- }
253
-
254
- private markLicenceExpired() {
255
- if (!AuthMiddleware.licenceExpired) {
256
- AuthMiddleware.licenceExpired = true;
257
- }
258
- if (!AuthMiddleware.shutdownTimer) {
259
- AuthMiddleware.shutdownTimer = setTimeout(() => {
260
- process.exit(1);
261
- }, 2 * 60 * 1000); // waiting two minutes after shutdowning server.
262
- }
263
- }
237
+ private async validateLicence(lic_data:any, publicKey:any){
238
+ try{
239
+ const token:any = await this.verifyJwt(lic_data, publicKey);
240
+ const licEndMs = this.normalizeEpochMs(token?.lic_end);
241
+ // Reject when licence end (epoch) is missing or already in the past.
242
+ if (!licEndMs) {
243
+ this.markLicenceExpired();
244
+ return{
245
+ status: false
246
+ }
247
+ }
248
+ if (licEndMs <= Date.now()) {
249
+ this.markLicenceExpired();
250
+ return {
251
+ status: false
252
+ }
253
+ } else {
254
+ AuthMiddleware.licenceValidatedUntilMs = licEndMs;
255
+ this.scheduleLicenceShutdown(licEndMs);
256
+ return {
257
+ status: true,
258
+ ttl: (licEndMs - Date.now()) > 0 ? (licEndMs - Date.now()) : null
259
+ }
260
+ }
261
+ }
262
+ catch(error:any){
263
+ this.markLicenceExpired();
264
+ return {
265
+ status: false
266
+ };
267
+ }
268
+ }
269
+
270
+ private markLicenceExpired() {
271
+ if (!AuthMiddleware.licenceExpired) {
272
+ AuthMiddleware.licenceExpired = true;
273
+ }
274
+ this.stopServer();
275
+ }
276
+
277
+ private scheduleLicenceShutdown(licEndMs: number) {
278
+ if (!licEndMs) {
279
+ return;
280
+ }
281
+ const delay = licEndMs - Date.now();
282
+ if (delay <= 0) {
283
+ this.markLicenceExpired();
284
+ return;
285
+ }
286
+ if (AuthMiddleware.licenceExpiryTimer) {
287
+ return;
288
+ }
289
+ AuthMiddleware.licenceExpiryTimer = setTimeout(() => {
290
+ this.markLicenceExpired();
291
+ }, delay);
292
+ }
293
+
294
+ private stopServer() {
295
+ if (AuthMiddleware.shutdownInitiated) {
296
+ return;
297
+ }
298
+ AuthMiddleware.shutdownInitiated = true;
299
+ setTimeout(() => {
300
+ try {
301
+ process.kill(process.pid, 'SIGTERM');
302
+ } catch {
303
+ process.exit(1);
304
+ }
305
+ if (!AuthMiddleware.shutdownTimer) {
306
+ AuthMiddleware.shutdownTimer = setTimeout(() => {
307
+ process.exit(1);
308
+ }, 5000);
309
+ }
310
+ }, 0);
311
+ }
264
312
 
265
313
  private normalizeEpochMs(epoch: number): number | null {
266
314
  if (!epoch || Number.isNaN(epoch)) {