@heavybit/pendoadmin-shared-lib 1.6.0 → 1.7.0

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.
Files changed (54) hide show
  1. package/dist/common/gateway-signature.d.ts +49 -0
  2. package/dist/common/gateway-signature.d.ts.map +1 -0
  3. package/dist/common/gateway-signature.js +99 -0
  4. package/dist/common/gateway-signature.js.map +1 -0
  5. package/dist/common/index.d.ts +1 -0
  6. package/dist/common/index.d.ts.map +1 -1
  7. package/dist/common/index.js +1 -0
  8. package/dist/common/index.js.map +1 -1
  9. package/dist/express/auth.middleware.d.ts +5 -4
  10. package/dist/express/auth.middleware.d.ts.map +1 -1
  11. package/dist/express/auth.middleware.js +49 -65
  12. package/dist/express/auth.middleware.js.map +1 -1
  13. package/dist/logging/index.d.ts +1 -0
  14. package/dist/logging/index.d.ts.map +1 -1
  15. package/dist/logging/index.js +5 -2
  16. package/dist/logging/index.js.map +1 -1
  17. package/dist/logging/redact.d.ts +16 -0
  18. package/dist/logging/redact.d.ts.map +1 -0
  19. package/dist/logging/redact.js +83 -0
  20. package/dist/logging/redact.js.map +1 -0
  21. package/dist/nestjs/auth.guard.d.ts +2 -0
  22. package/dist/nestjs/auth.guard.d.ts.map +1 -1
  23. package/dist/nestjs/auth.guard.js +11 -3
  24. package/dist/nestjs/auth.guard.js.map +1 -1
  25. package/dist/nestjs/permission.guard.js +5 -4
  26. package/dist/nestjs/permission.guard.js.map +1 -1
  27. package/dist/rate-limit/create-rate-limiter.d.ts +7 -1
  28. package/dist/rate-limit/create-rate-limiter.d.ts.map +1 -1
  29. package/dist/rate-limit/create-rate-limiter.js +12 -2
  30. package/dist/rate-limit/create-rate-limiter.js.map +1 -1
  31. package/dist/rate-limit/index.d.ts +1 -1
  32. package/dist/rate-limit/index.d.ts.map +1 -1
  33. package/dist/rate-limit/index.js +1 -1
  34. package/dist/rate-limit/index.js.map +1 -1
  35. package/dist/types/index.d.ts +2 -0
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/dist/types/index.js +2 -0
  38. package/dist/types/index.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/common/gateway-signature.ts +124 -0
  41. package/src/common/index.ts +1 -0
  42. package/src/express/auth.middleware.ts +46 -64
  43. package/src/logging/index.ts +6 -2
  44. package/src/logging/redact.ts +84 -0
  45. package/src/nestjs/auth.guard.ts +12 -3
  46. package/src/nestjs/permission.guard.ts +5 -5
  47. package/src/rate-limit/create-rate-limiter.ts +13 -3
  48. package/src/rate-limit/index.ts +1 -1
  49. package/src/types/index.ts +2 -0
  50. package/tests/auth-middleware.test.mjs +91 -0
  51. package/tests/gateway-signature.test.mjs +93 -0
  52. package/tests/nestjs-guards.test.mjs +45 -0
  53. package/tests/rate-limit-key.test.mjs +25 -0
  54. package/tests/redact.test.mjs +61 -0
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  import { Injectable, ForbiddenException, } from '@nestjs/common';
11
11
  import { Reflector } from '@nestjs/core';
12
12
  import { PERMISSIONS_KEY, REQUIRE_ALL_KEY, ROLES_KEY, } from './permission.decorator.js';
13
- import { IS_PUBLIC_KEY } from './auth.guard.js';
13
+ import { IS_PUBLIC_KEY, GATEWAY_VERIFIED_FLAG } from './auth.guard.js';
14
14
  /**
15
15
  * NestJS Guard: checks permissions/roles from decorators.
16
16
  * Must be used after AuthGuard so that request.user is populated.
@@ -37,9 +37,10 @@ let PermissionGuard = class PermissionGuard {
37
37
  if (user.isSuperAdmin)
38
38
  return true;
39
39
  // M2M / gateway-authenticated clients: the gateway already verified the JWT
40
- // and injected headers. Requiring human roles on top of scoped permissions
41
- // is overly restrictive for API access.
42
- const isGatewayAuth = request.headers?.['x-gateway-auth'] === 'verified';
40
+ // and signed the injected headers (AuthGuard checked the signature).
41
+ // Requiring human roles on top of scoped permissions is overly restrictive
42
+ // for API access.
43
+ const isGatewayAuth = request[GATEWAY_VERIFIED_FLAG] === true;
43
44
  // Check permissions first — they are the primary authorization mechanism
44
45
  const requiredPermissions = this.reflector.getAllAndOverride(PERMISSIONS_KEY, [context.getHandler(), context.getClass()]);
45
46
  const requireAll = this.reflector.getAllAndOverride(REQUIRE_ALL_KEY, [context.getHandler(), context.getClass()]);
@@ -1 +1 @@
1
- {"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../src/nestjs/permission.guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permission.guard.js","sourceRoot":"","sources":["../../src/nestjs/permission.guard.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ export interface RateLimitConfig {
10
10
  keyPrefix?: string;
11
11
  /** Optional Redis client — falls back to in-memory if omitted */
12
12
  redisClient?: Redis | null;
13
- /** Extract the rate-limit key from the request. Default: X-User-Id header → IP → "unknown" */
13
+ /** Extract the rate-limit key from the request. Default: signed gateway user id → IP → "unknown" */
14
14
  keyExtractor?: (req: Request) => string;
15
15
  }
16
16
  export interface RateLimitResult {
@@ -20,4 +20,10 @@ export interface RateLimitResult {
20
20
  middleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
21
21
  }
22
22
  export declare function createRateLimiter(cfg?: RateLimitConfig): RateLimitResult;
23
+ /**
24
+ * The gateway user id only when its signature verifies — an unsigned
25
+ * x-user-id is caller-controlled, and a fresh value per request would get a
26
+ * fresh budget. Otherwise the client IP.
27
+ */
28
+ export declare function defaultKeyExtractor(req: Request): string;
23
29
  //# sourceMappingURL=create-rate-limiter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limit/create-rate-limiter.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAuC,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAIjC,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,WAAW,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IAC3B,8FAA8F;IAC9F,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,yBAAyB;IACzB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAID,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,eAAoB,GAAG,eAAe,CAmE5E"}
1
+ {"version":3,"file":"create-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limit/create-rate-limiter.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAuC,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAKjC,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,WAAW,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IAC3B,oGAAoG;IACpG,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,yBAAyB;IACzB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAID,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,eAAoB,GAAG,eAAe,CAmE5E;AAID;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAMxD"}
@@ -3,6 +3,7 @@
3
3
  // Factory that creates a dual-layer (Redis + memory fallback) rate limiter.
4
4
  // Available for any Express-based service.
5
5
  import { RateLimiterRedis, RateLimiterMemory } from "rate-limiter-flexible";
6
+ import { isTrustedGatewayIdentity } from "../common/gateway-signature.js";
6
7
  // ─── Factory ────────────────────────────────────────────
7
8
  export function createRateLimiter(cfg = {}) {
8
9
  const { maxRequests = 100, windowMs = 60_000, keyPrefix = "rl:service", redisClient = null, keyExtractor = defaultKeyExtractor, } = cfg;
@@ -54,7 +55,16 @@ export function createRateLimiter(cfg = {}) {
54
55
  return { limiter, middleware };
55
56
  }
56
57
  // ─── Defaults ───────────────────────────────────────────
57
- function defaultKeyExtractor(req) {
58
- return req.headers["x-user-id"] || req.ip || "unknown";
58
+ /**
59
+ * The gateway user id only when its signature verifies — an unsigned
60
+ * x-user-id is caller-controlled, and a fresh value per request would get a
61
+ * fresh budget. Otherwise the client IP.
62
+ */
63
+ export function defaultKeyExtractor(req) {
64
+ const userId = req.headers["x-user-id"];
65
+ if (typeof userId === "string" && userId && isTrustedGatewayIdentity(req.headers)) {
66
+ return `user:${userId}`;
67
+ }
68
+ return `ip:${req.ip || "unknown"}`;
59
69
  }
60
70
  //# sourceMappingURL=create-rate-limiter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-rate-limiter.js","sourceRoot":"","sources":["../../src/rate-limit/create-rate-limiter.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,4EAA4E;AAC5E,2CAA2C;AAG3C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAuB,MAAM,uBAAuB,CAAC;AAyBjG,2DAA2D;AAE3D,MAAM,UAAU,iBAAiB,CAAC,MAAuB,EAAE;IACzD,MAAM,EACJ,WAAW,GAAG,GAAG,EACjB,QAAQ,GAAG,MAAM,EACjB,SAAS,GAAG,YAAY,EACxB,WAAW,GAAG,IAAI,EAClB,YAAY,GAAG,mBAAmB,GACnC,GAAG,GAAG,CAAC;IAER,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC;IAE9D,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC;QAC1C,SAAS;QACT,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,YAAY;KACvB,CAAC,CAAC;IAEH,IAAI,OAA4B,CAAC;IAEjC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,GAAG,IAAI,gBAAgB,CAAC;YAC7B,WAAW,EAAE,WAAW;YACxB,SAAS;YACT,MAAM,EAAE,WAAW;YACnB,QAAQ,EAAE,YAAY;YACtB,aAAa,EAAE,CAAC;YAChB,gBAAgB,EAAE,aAAa;SAChC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,aAAa,CAAC;IAC1B,CAAC;IAED,wDAAwD;IAExD,KAAK,UAAU,UAAU,CACvB,GAAY,EACZ,GAAa,EACb,IAAkB;QAElB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAE1C,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;YAChD,GAAG,CAAC,SAAS,CAAC,uBAAuB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;YAC/D,GAAG,CAAC,SAAS,CACX,mBAAmB,EACnB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CACzD,CAAC;YACF,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,MAAW,EAAE,CAAC;YACrB,iDAAiD;YACjD,IAAI,OAAO,MAAM,EAAE,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC7C,OAAO,CAAC,KAAK,CAAC,cAAc,SAAS,qBAAqB,EAAE,MAAM,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC;gBACvF,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YACD,GAAG,CAAC,SAAS,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;YAChD,GAAG,CAAC,SAAS,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;YAC1C,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;YACpE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,4CAA4C;aACtD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACjC,CAAC;AAED,2DAA2D;AAE3D,SAAS,mBAAmB,CAAC,GAAY;IACvC,OAAQ,GAAG,CAAC,OAAO,CAAC,WAAW,CAAY,IAAI,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"create-rate-limiter.js","sourceRoot":"","sources":["../../src/rate-limit/create-rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export { createRateLimiter } from "./create-rate-limiter.js";
1
+ export { createRateLimiter, defaultKeyExtractor } from "./create-rate-limiter.js";
2
2
  export type { RateLimitConfig, RateLimitResult } from "./create-rate-limiter.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1,2 +1,2 @@
1
- export { createRateLimiter } from "./create-rate-limiter.js";
1
+ export { createRateLimiter, defaultKeyExtractor } from "./create-rate-limiter.js";
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -213,5 +213,7 @@ export declare const GATEWAY_HEADERS: {
213
213
  readonly IS_SUPERADMIN: "x-is-superadmin";
214
214
  readonly CORRELATION_ID: "x-correlation-id";
215
215
  readonly SERVICE_NAME: "x-service-name";
216
+ readonly GATEWAY_SIGNATURE: "x-gateway-signature";
217
+ readonly GATEWAY_TIMESTAMP: "x-gateway-ts";
216
218
  };
217
219
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -48,5 +48,7 @@ export const GATEWAY_HEADERS = {
48
48
  IS_SUPERADMIN: 'x-is-superadmin',
49
49
  CORRELATION_ID: 'x-correlation-id',
50
50
  SERVICE_NAME: 'x-service-name',
51
+ GATEWAY_SIGNATURE: 'x-gateway-signature',
52
+ GATEWAY_TIMESTAMP: 'x-gateway-ts',
51
53
  };
52
54
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,oBAAoB;AACpB,iDAAiD;AA2EjD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAe,EACf,kBAAkC;IAElC,IAAI,IAAI,CAAC,YAAY,IAAI,kBAAkB,EAAE,CAAC;QAC5C,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,sCAAsC,CAAC;AAE5E,8FAA8F;AAC9F,MAAM,UAAU,qBAAqB,CACnC,IAA2D;IAE3D,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,qBAAqB,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;AACnF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAmD,EACnD,kBAAkC;IAElC,IAAI,qBAAqB,CAAC,IAAI,CAAC;QAAE,OAAO,kBAAkB,IAAI,SAAS,CAAC;IACxE,IAAI,CAAC,IAAI,CAAC,SAAS;QAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACtG,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC;AAwJD,iDAAiD;AACjD,uBAAuB;AACvB,iDAAiD;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,WAAW;IACpB,UAAU,EAAE,cAAc;IAC1B,UAAU,EAAE,cAAc;IAC1B,UAAU,EAAE,cAAc;IAC1B,gBAAgB,EAAE,oBAAoB;IACtC,aAAa,EAAE,iBAAiB;IAChC,cAAc,EAAE,kBAAkB;IAClC,YAAY,EAAE,gBAAgB;CACtB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,oBAAoB;AACpB,iDAAiD;AA2EjD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAe,EACf,kBAAkC;IAElC,IAAI,IAAI,CAAC,YAAY,IAAI,kBAAkB,EAAE,CAAC;QAC5C,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,sCAAsC,CAAC;AAE5E,8FAA8F;AAC9F,MAAM,UAAU,qBAAqB,CACnC,IAA2D;IAE3D,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,qBAAqB,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;AACnF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAmD,EACnD,kBAAkC;IAElC,IAAI,qBAAqB,CAAC,IAAI,CAAC;QAAE,OAAO,kBAAkB,IAAI,SAAS,CAAC;IACxE,IAAI,CAAC,IAAI,CAAC,SAAS;QAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACtG,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC;AAwJD,iDAAiD;AACjD,uBAAuB;AACvB,iDAAiD;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,WAAW;IACpB,UAAU,EAAE,cAAc;IAC1B,UAAU,EAAE,cAAc;IAC1B,UAAU,EAAE,cAAc;IAC1B,gBAAgB,EAAE,oBAAoB;IACtC,aAAa,EAAE,iBAAiB;IAChC,cAAc,EAAE,kBAAkB;IAClC,YAAY,EAAE,gBAAgB;IAC9B,iBAAiB,EAAE,qBAAqB;IACxC,iBAAiB,EAAE,cAAc;CACzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heavybit/pendoadmin-shared-lib",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Shared library for PendoAdmin microservices",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,7 +29,7 @@
29
29
  "build": "tsc",
30
30
  "dev": "tsc --watch",
31
31
  "clean": "rm -rf dist",
32
- "test": "npm run build && node --test tests/"
32
+ "test": "npm run build && node --test tests/*.test.mjs"
33
33
  },
34
34
  "exports": {
35
35
  ".": "./dist/index.js",
@@ -0,0 +1,124 @@
1
+ import crypto from 'node:crypto';
2
+
3
+ /**
4
+ * Signed gateway identity.
5
+ *
6
+ * The api-gateway (and any internal service forwarding a caller's identity)
7
+ * signs the identity headers it injects with GATEWAY_SIGNING_SECRET. Downstream
8
+ * services verify the signature before trusting x-user-id / x-is-superadmin /
9
+ * x-user-permissions, so a request that reaches a service port directly, or
10
+ * slips past the gateway with its own headers, cannot impersonate anyone.
11
+ *
12
+ * The api-gateway keeps its own copy of the signer
13
+ * (api-gateway/src/utils/gateway-signature.ts). Any change to the canonical
14
+ * string below must be made in both places — the shared test vector in both
15
+ * test suites pins the format.
16
+ */
17
+
18
+ export const GATEWAY_SIGNATURE_HEADER = 'x-gateway-signature';
19
+ export const GATEWAY_TIMESTAMP_HEADER = 'x-gateway-ts';
20
+ const SIGNATURE_VERSION = 'v1';
21
+
22
+ /** Identity headers covered by the signature, in canonical order. */
23
+ export const GATEWAY_IDENTITY_HEADERS = [
24
+ 'x-user-id',
25
+ 'x-user-email',
26
+ 'x-company-id',
27
+ 'x-company-slug',
28
+ 'x-user-roles',
29
+ 'x-user-permissions',
30
+ 'x-is-superadmin',
31
+ 'x-otp-verified',
32
+ ] as const;
33
+
34
+ /** Default allowed clock skew between signer and verifier. */
35
+ export const GATEWAY_SIGNATURE_MAX_SKEW_SECONDS = 60;
36
+
37
+ type HeaderBag = Record<string, string | string[] | undefined>;
38
+
39
+ function headerValue(headers: HeaderBag, name: string): string {
40
+ const value = headers[name];
41
+ return (Array.isArray(value) ? value[0] : value) ?? '';
42
+ }
43
+
44
+ function canonicalString(headers: HeaderBag, timestamp: string): string {
45
+ return [
46
+ SIGNATURE_VERSION,
47
+ timestamp,
48
+ headerValue(headers, 'x-correlation-id'),
49
+ ...GATEWAY_IDENTITY_HEADERS.map((name) => headerValue(headers, name)),
50
+ ].join('\n');
51
+ }
52
+
53
+ function hmac(secret: string, payload: string): string {
54
+ return crypto.createHmac('sha256', secret).update(payload).digest('hex');
55
+ }
56
+
57
+ /**
58
+ * Signature headers for the identity headers already present in `headers`
59
+ * (lower-case names). Merge the result into the outgoing request headers.
60
+ */
61
+ export function signGatewayIdentity(
62
+ headers: HeaderBag,
63
+ secret: string,
64
+ nowMs: number = Date.now(),
65
+ ): Record<typeof GATEWAY_SIGNATURE_HEADER | typeof GATEWAY_TIMESTAMP_HEADER, string> {
66
+ const timestamp = String(Math.floor(nowMs / 1000));
67
+ return {
68
+ [GATEWAY_SIGNATURE_HEADER]: hmac(secret, canonicalString(headers, timestamp)),
69
+ [GATEWAY_TIMESTAMP_HEADER]: timestamp,
70
+ };
71
+ }
72
+
73
+ export interface VerifyGatewayIdentityOptions {
74
+ maxSkewSeconds?: number;
75
+ nowMs?: number;
76
+ }
77
+
78
+ /** True when the identity headers carry a valid, fresh signature from `secret`. */
79
+ export function verifyGatewayIdentity(
80
+ headers: HeaderBag,
81
+ secret: string,
82
+ options: VerifyGatewayIdentityOptions = {},
83
+ ): boolean {
84
+ if (!secret) return false;
85
+ const { maxSkewSeconds = GATEWAY_SIGNATURE_MAX_SKEW_SECONDS, nowMs = Date.now() } = options;
86
+
87
+ const timestamp = headerValue(headers, GATEWAY_TIMESTAMP_HEADER);
88
+ const signature = headerValue(headers, GATEWAY_SIGNATURE_HEADER);
89
+ if (!/^\d+$/.test(timestamp) || !/^[0-9a-f]{64}$/.test(signature)) return false;
90
+ if (Math.abs(nowMs / 1000 - Number(timestamp)) > maxSkewSeconds) return false;
91
+
92
+ const expected = Buffer.from(hmac(secret, canonicalString(headers, timestamp)), 'hex');
93
+ const provided = Buffer.from(signature, 'hex');
94
+ return expected.length === provided.length && crypto.timingSafeEqual(expected, provided);
95
+ }
96
+
97
+ /**
98
+ * The secret services sign and verify gateway identity with. Unset means
99
+ * unsigned identity is accepted only outside production (see
100
+ * isUnsignedGatewayIdentityAllowed).
101
+ */
102
+ export function gatewaySigningSecret(): string {
103
+ return process.env.GATEWAY_SIGNING_SECRET || '';
104
+ }
105
+
106
+ /** Local development may run services without a signing secret; production never trusts unsigned identity. */
107
+ export function isUnsignedGatewayIdentityAllowed(): boolean {
108
+ return !gatewaySigningSecret() && process.env.NODE_ENV !== 'production';
109
+ }
110
+
111
+ /**
112
+ * Whether a request's gateway identity headers can be trusted: a valid
113
+ * signature when a secret is configured, otherwise only outside production.
114
+ */
115
+ export function isTrustedGatewayIdentity(headers: HeaderBag): boolean {
116
+ const secret = gatewaySigningSecret();
117
+ if (secret) return verifyGatewayIdentity(headers, secret);
118
+ return isUnsignedGatewayIdentityAllowed();
119
+ }
120
+
121
+ /** Direct JWT validation is a development convenience only; production requests must come through the gateway. */
122
+ export function isDirectJwtFallbackAllowed(): boolean {
123
+ return process.env.NODE_ENV !== 'production';
124
+ }
@@ -5,3 +5,4 @@ export * from './uuid.utils.js';
5
5
  export * from './response.utils.js';
6
6
  export * from './timezone.utils.js';
7
7
  export * from './permission.utils.js';
8
+ export * from './gateway-signature.js';
@@ -1,5 +1,6 @@
1
1
  import type { Request, Response, NextFunction } from 'express';
2
2
  import { verifyToken, extractBearerToken } from '../common/jwt.utils.js';
3
+ import { isTrustedGatewayIdentity, isDirectJwtFallbackAllowed } from '../common/gateway-signature.js';
3
4
  import { GATEWAY_HEADERS, type IAuthUser } from '../types/index.js';
4
5
 
5
6
  /** Parse a header value that may be a JSON array string or comma-separated. */
@@ -21,83 +22,64 @@ declare global {
21
22
  }
22
23
  }
23
24
 
25
+ /** The caller from signed gateway headers, or null when absent or unsigned/forged. */
26
+ function userFromGatewayHeaders(req: Request): IAuthUser | null {
27
+ const userId = req.headers[GATEWAY_HEADERS.USER_ID] as string;
28
+ if (!userId || !isTrustedGatewayIdentity(req.headers)) return null;
29
+ return {
30
+ id: userId,
31
+ email: req.headers[GATEWAY_HEADERS.USER_EMAIL] as string || '',
32
+ companyId: req.headers[GATEWAY_HEADERS.COMPANY_ID] as string || '',
33
+ roles: parseArrayHeader(req.headers[GATEWAY_HEADERS.USER_ROLES] as string),
34
+ permissions: parseArrayHeader(req.headers[GATEWAY_HEADERS.USER_PERMISSIONS] as string),
35
+ isSuperAdmin: req.headers[GATEWAY_HEADERS.IS_SUPERADMIN] === 'true',
36
+ };
37
+ }
38
+
39
+ /** The caller from a bearer JWT — development only (direct service access). */
40
+ function userFromBearerToken(req: Request, jwtSecret?: string): IAuthUser | null {
41
+ if (!jwtSecret || !isDirectJwtFallbackAllowed()) return null;
42
+ const token = extractBearerToken(req.headers.authorization);
43
+ if (!token) return null;
44
+ try {
45
+ const payload = verifyToken(token, jwtSecret);
46
+ return {
47
+ id: payload.sub,
48
+ email: payload.email,
49
+ companyId: payload.companyId,
50
+ roles: payload.roles || [],
51
+ permissions: payload.permissions || [],
52
+ isSuperAdmin: payload.isSuperAdmin || false,
53
+ };
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
24
59
  /**
25
60
  * Auth middleware for downstream services (behind API gateway).
26
- * Reads user context from gateway-injected headers.
27
- * Falls back to JWT validation if headers are missing (direct access).
61
+ * Reads user context from gateway-injected headers, trusted only with a valid
62
+ * gateway signature (GATEWAY_SIGNING_SECRET). Outside production it falls back
63
+ * to direct JWT validation for local development.
28
64
  */
29
65
  export function authenticateFromGateway(jwtSecret?: string) {
30
- return (req: Request, _res: Response, next: NextFunction): void => {
31
- // Method 1: Read gateway-injected headers
32
- const userId = req.headers[GATEWAY_HEADERS.USER_ID] as string;
33
- if (userId) {
34
- req.user = {
35
- id: userId,
36
- email: req.headers[GATEWAY_HEADERS.USER_EMAIL] as string || '',
37
- companyId: req.headers[GATEWAY_HEADERS.COMPANY_ID] as string || '',
38
- roles: parseArrayHeader(req.headers[GATEWAY_HEADERS.USER_ROLES] as string),
39
- permissions: parseArrayHeader(req.headers[GATEWAY_HEADERS.USER_PERMISSIONS] as string),
40
- isSuperAdmin: req.headers[GATEWAY_HEADERS.IS_SUPERADMIN] === 'true',
41
- };
66
+ return (req: Request, res: Response, next: NextFunction): void => {
67
+ const user = userFromGatewayHeaders(req) ?? userFromBearerToken(req, jwtSecret);
68
+ if (user) {
69
+ req.user = user;
42
70
  return next();
43
71
  }
44
-
45
- // Method 2: Direct JWT validation (for development / direct access)
46
- if (jwtSecret) {
47
- const token = extractBearerToken(req.headers.authorization);
48
- if (token) {
49
- try {
50
- const payload = verifyToken(token, jwtSecret);
51
- req.user = {
52
- id: payload.sub,
53
- email: payload.email,
54
- companyId: payload.companyId,
55
- roles: payload.roles || [],
56
- permissions: payload.permissions || [],
57
- isSuperAdmin: payload.isSuperAdmin || false,
58
- };
59
- return next();
60
- } catch {
61
- // Fall through to 401
62
- }
63
- }
64
- }
65
-
66
- _res.status(401).json({ success: false, message: 'Authentication required' });
72
+ res.status(401).json({ success: false, message: 'Authentication required' });
67
73
  };
68
74
  }
69
75
 
70
76
  /**
71
- * Optional auth — attaches user if present, continues if not.
77
+ * Optional auth — attaches user if present (and trusted), continues if not.
72
78
  */
73
79
  export function optionalAuthFromGateway(jwtSecret?: string) {
74
80
  return (req: Request, _res: Response, next: NextFunction): void => {
75
- const userId = req.headers[GATEWAY_HEADERS.USER_ID] as string;
76
- if (userId) {
77
- req.user = {
78
- id: userId,
79
- email: req.headers[GATEWAY_HEADERS.USER_EMAIL] as string || '',
80
- companyId: req.headers[GATEWAY_HEADERS.COMPANY_ID] as string || '',
81
- roles: parseArrayHeader(req.headers[GATEWAY_HEADERS.USER_ROLES] as string),
82
- permissions: parseArrayHeader(req.headers[GATEWAY_HEADERS.USER_PERMISSIONS] as string),
83
- isSuperAdmin: req.headers[GATEWAY_HEADERS.IS_SUPERADMIN] === 'true',
84
- };
85
- } else if (jwtSecret) {
86
- const token = extractBearerToken(req.headers.authorization);
87
- if (token) {
88
- try {
89
- const payload = verifyToken(token, jwtSecret);
90
- req.user = {
91
- id: payload.sub,
92
- email: payload.email,
93
- companyId: payload.companyId,
94
- roles: payload.roles || [],
95
- permissions: payload.permissions || [],
96
- isSuperAdmin: payload.isSuperAdmin || false,
97
- };
98
- } catch { /* ignore */ }
99
- }
100
- }
81
+ const user = userFromGatewayHeaders(req) ?? userFromBearerToken(req, jwtSecret);
82
+ if (user) req.user = user;
101
83
  next();
102
84
  };
103
85
  }
@@ -2,6 +2,9 @@ import winston from 'winston';
2
2
  import DailyRotateFile from 'winston-daily-rotate-file';
3
3
  import { mkdirSync } from 'fs';
4
4
  import { resolve } from 'path';
5
+ import { redactFormat } from './redact.js';
6
+
7
+ export { redactFormat, redactSensitive, isSensitiveKey, REDACTED } from './redact.js';
5
8
 
6
9
  const { combine, timestamp, json, errors, colorize, printf, splat } = winston.format;
7
10
 
@@ -118,7 +121,8 @@ export function createLogger(service: string, level?: string): winston.Logger {
118
121
  return winston.createLogger({
119
122
  level: resolvedLevel,
120
123
  defaultMeta: { service },
121
- format: combine(errors({ stack: true }), splat(), timestamp()),
124
+ // redactFormat runs after splat so metadata passed as extra arguments is covered too.
125
+ format: combine(errors({ stack: true }), splat(), redactFormat(), timestamp()),
122
126
  transports,
123
127
  });
124
128
  }
@@ -137,7 +141,7 @@ export function createAuditLogger(service: string): winston.Logger {
137
141
  return winston.createLogger({
138
142
  level: 'info',
139
143
  defaultMeta: { service, type: 'audit' },
140
- format: combine(splat(), timestamp(), json()),
144
+ format: combine(splat(), redactFormat(), timestamp(), json()),
141
145
  transports,
142
146
  });
143
147
  }
@@ -0,0 +1,84 @@
1
+ import winston from 'winston';
2
+
3
+ export const REDACTED = '[REDACTED]';
4
+
5
+ // Exact keys (compared lower-cased, with '-' and '_' removed) and key fragments
6
+ // whose values must never reach a log: credentials, tokens, OTPs, secrets.
7
+ const EXACT_KEYS = new Set([
8
+ 'authorization',
9
+ 'proxyauthorization',
10
+ 'cookie',
11
+ 'setcookie',
12
+ 'password',
13
+ 'passwd',
14
+ 'currentpassword',
15
+ 'newpassword',
16
+ 'apikey',
17
+ 'otp',
18
+ 'pin',
19
+ 'smtppassword',
20
+ 'encryptionkey',
21
+ 'privatekey',
22
+ 'xinternalservicesecret',
23
+ 'xgatewaysignature',
24
+ ]);
25
+ const KEY_FRAGMENTS = ['secret', 'password', 'passphrase', 'credential'];
26
+ // accessToken, refresh_token, captchaToken… but not counters like promptTokens.
27
+ const KEY_SUFFIXES = ['token'];
28
+
29
+ const MAX_DEPTH = 8;
30
+
31
+ /** True for keys whose values are redacted (case/separator-insensitive). */
32
+ export function isSensitiveKey(key: string): boolean {
33
+ const normalized = key.toLowerCase().replace(/[-_]/g, '');
34
+ return EXACT_KEYS.has(normalized)
35
+ || KEY_FRAGMENTS.some((fragment) => normalized.includes(fragment))
36
+ || KEY_SUFFIXES.some((suffix) => normalized.endsWith(suffix));
37
+ }
38
+
39
+ // Bearer/Basic credentials embedded in free text (e.g. a logged header line or error message).
40
+ const INLINE_CREDENTIAL = /\b(Bearer|Basic)\s+[A-Za-z0-9\-._~+/]+=*/g;
41
+
42
+ function redactString(value: string): string {
43
+ return value.replace(INLINE_CREDENTIAL, `$1 ${REDACTED}`);
44
+ }
45
+
46
+ /**
47
+ * Deep copy of `value` with sensitive keys replaced by [REDACTED] and inline
48
+ * bearer/basic credentials masked. Errors keep their message and stack.
49
+ * Handles cycles and caps depth.
50
+ */
51
+ export function redactSensitive(value: unknown, depth = 0, seen: WeakSet<object> = new WeakSet()): unknown {
52
+ if (typeof value === 'string') return redactString(value);
53
+ if (value === null || typeof value !== 'object') return value;
54
+ if (depth >= MAX_DEPTH) return '[Truncated]';
55
+ if (seen.has(value)) return '[Circular]';
56
+ seen.add(value);
57
+
58
+ if (Array.isArray(value)) return value.map((item) => redactSensitive(item, depth + 1, seen));
59
+ if (value instanceof Date || Buffer.isBuffer(value)) return value;
60
+
61
+ const source = value as Record<string, unknown>;
62
+ const result: Record<string, unknown> = {};
63
+ if (value instanceof Error) {
64
+ result.name = value.name;
65
+ result.message = redactString(value.message);
66
+ if (value.stack) result.stack = redactString(value.stack);
67
+ }
68
+ for (const key of Object.keys(source)) {
69
+ result[key] = isSensitiveKey(key) ? REDACTED : redactSensitive(source[key], depth + 1, seen);
70
+ }
71
+ return result;
72
+ }
73
+
74
+ /**
75
+ * Winston format that redacts every log entry's message and metadata in place.
76
+ * Winston's internal Symbol keys (level/message/splat) are left untouched.
77
+ */
78
+ export const redactFormat = winston.format((info) => {
79
+ for (const key of Object.keys(info)) {
80
+ if (key === 'level') continue;
81
+ info[key] = isSensitiveKey(key) ? REDACTED : redactSensitive(info[key]);
82
+ }
83
+ return info;
84
+ });
@@ -6,6 +6,7 @@ import {
6
6
  } from '@nestjs/common';
7
7
  import { Reflector } from '@nestjs/core';
8
8
  import { verifyToken } from '../common/jwt.utils.js';
9
+ import { isTrustedGatewayIdentity, isDirectJwtFallbackAllowed } from '../common/gateway-signature.js';
9
10
  import { GATEWAY_HEADERS, type IAuthUser } from '../types/index.js';
10
11
 
11
12
  /** Parse a header value that may be a JSON array string or comma-separated. */
@@ -20,6 +21,9 @@ function parseArrayHeader(value: string | undefined): string[] {
20
21
 
21
22
  export const IS_PUBLIC_KEY = 'isPublic';
22
23
 
24
+ /** Request property AuthGuard sets when the caller came from signed gateway headers. */
25
+ export const GATEWAY_VERIFIED_FLAG = 'gatewayVerified';
26
+
23
27
  /**
24
28
  * NestJS Guard: validates JWT or trusts gateway-injected headers.
25
29
  */
@@ -36,9 +40,10 @@ export class AuthGuard implements CanActivate {
36
40
 
37
41
  const request = context.switchToHttp().getRequest();
38
42
 
39
- // Trust gateway-injected headers first
43
+ // Trust gateway-injected headers only with a valid gateway signature
40
44
  const gatewayUserId = request.headers[GATEWAY_HEADERS.USER_ID];
41
- if (gatewayUserId) {
45
+ if (gatewayUserId && isTrustedGatewayIdentity(request.headers)) {
46
+ request[GATEWAY_VERIFIED_FLAG] = true;
42
47
  request.user = {
43
48
  id: gatewayUserId,
44
49
  companyId: request.headers[GATEWAY_HEADERS.COMPANY_ID] || undefined,
@@ -51,7 +56,11 @@ export class AuthGuard implements CanActivate {
51
56
  return true;
52
57
  }
53
58
 
54
- // Fallback to direct JWT validation
59
+ // Fallback to direct JWT validation — development only; production
60
+ // requests must arrive through the gateway.
61
+ if (!isDirectJwtFallbackAllowed()) {
62
+ throw new UnauthorizedException('Authentication required');
63
+ }
55
64
  const authHeader = request.headers.authorization;
56
65
  if (!authHeader?.startsWith('Bearer ')) {
57
66
  throw new UnauthorizedException('Missing authentication token');