@discover-cloud/shared 1.2.9 → 1.4.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 (183) hide show
  1. package/dist/authorization/index.d.ts +2 -2
  2. package/dist/authorization/index.js +1 -1
  3. package/dist/authorization/permission-cache.service.d.ts +104 -16
  4. package/dist/authorization/permission-cache.service.js +177 -148
  5. package/dist/authorization/permission-checker.d.ts +30 -0
  6. package/dist/authorization/permission-checker.js +40 -0
  7. package/dist/context/index.d.ts +1 -1
  8. package/dist/context/index.js +1 -1
  9. package/dist/context/request-context.storage.d.ts +21 -0
  10. package/dist/context/request-context.storage.js +37 -0
  11. package/dist/contracts/auth-service/account.dto.d.ts +30 -0
  12. package/dist/contracts/auth-service/account.dto.js +7 -0
  13. package/dist/contracts/auth-service/auth.dto.d.ts +21 -0
  14. package/dist/contracts/auth-service/auth.dto.js +10 -0
  15. package/dist/contracts/index.d.ts +1 -0
  16. package/dist/{security → contracts}/index.js +1 -1
  17. package/dist/contracts/visibility.d.ts +63 -0
  18. package/dist/contracts/visibility.js +10 -0
  19. package/dist/dtos/audit-service.types.d.ts +42 -0
  20. package/dist/dtos/audit-service.types.js +31 -0
  21. package/dist/dtos/auth-service.dto.d.ts +87 -26
  22. package/dist/dtos/auth-service.dto.js +4 -0
  23. package/dist/dtos/cloud-service.dto.d.ts +57 -27
  24. package/dist/dtos/cloud-service.dto.js +5 -0
  25. package/dist/dtos/index.d.ts +6 -5
  26. package/dist/dtos/index.js +1 -0
  27. package/dist/dtos/insights-service.dto.d.ts +184 -32
  28. package/dist/dtos/insights-service.dto.js +8 -0
  29. package/dist/dtos/response.dto.d.ts +12 -83
  30. package/dist/dtos/response.dto.js +2 -21
  31. package/dist/dtos/user-service.dto.d.ts +163 -13
  32. package/dist/enums/domain.enums.d.ts +272 -75
  33. package/dist/enums/domain.enums.js +329 -102
  34. package/dist/enums/index.d.ts +2 -2
  35. package/dist/enums/permissions.enums.d.ts +142 -80
  36. package/dist/enums/permissions.enums.js +174 -133
  37. package/dist/errors/app-error.d.ts +13 -18
  38. package/dist/errors/app-error.js +15 -21
  39. package/dist/errors/http-errors.d.ts +106 -18
  40. package/dist/errors/http-errors.js +136 -51
  41. package/dist/errors/index.d.ts +2 -2
  42. package/dist/http/index.d.ts +2 -1
  43. package/dist/http/index.js +1 -0
  44. package/dist/http/request-context.d.ts +8 -0
  45. package/dist/http/request-context.js +37 -0
  46. package/dist/http/service-client.d.ts +93 -40
  47. package/dist/http/service-client.js +104 -55
  48. package/dist/index.d.ts +11 -9
  49. package/dist/index.js +2 -0
  50. package/dist/jwt/index.d.ts +3 -2
  51. package/dist/jwt/index.js +2 -1
  52. package/dist/jwt/machine-jwt-verifier.d.ts +21 -0
  53. package/dist/jwt/machine-jwt-verifier.js +83 -0
  54. package/dist/jwt/machine-token-client.d.ts +36 -5
  55. package/dist/jwt/machine-token-client.js +61 -27
  56. package/dist/jwt/user-jwt-verifier.d.ts +31 -0
  57. package/dist/jwt/user-jwt-verifier.js +102 -0
  58. package/dist/messaging/audit.publisher.d.ts +14 -0
  59. package/dist/messaging/audit.publisher.js +49 -0
  60. package/dist/messaging/index.d.ts +2 -0
  61. package/dist/{dto → messaging}/index.js +2 -3
  62. package/dist/messaging/rabbitmq.client.d.ts +15 -0
  63. package/dist/messaging/rabbitmq.client.js +133 -0
  64. package/dist/middleware/error-handler.middleware.d.ts +8 -28
  65. package/dist/middleware/error-handler.middleware.js +63 -54
  66. package/dist/middleware/index.d.ts +10 -8
  67. package/dist/middleware/index.js +7 -5
  68. package/dist/middleware/request-context.middleware.d.ts +6 -0
  69. package/dist/middleware/request-context.middleware.js +48 -0
  70. package/dist/middleware/require-auth.middleware.d.ts +7 -76
  71. package/dist/middleware/require-auth.middleware.js +29 -117
  72. package/dist/middleware/require-machine.middleware.d.ts +13 -0
  73. package/dist/middleware/require-machine.middleware.js +128 -0
  74. package/dist/middleware/require-org-permission-from-body.middleware.d.ts +10 -0
  75. package/dist/middleware/require-org-permission-from-body.middleware.js +23 -0
  76. package/dist/middleware/require-org-permission.middleware.d.ts +19 -0
  77. package/dist/middleware/require-org-permission.middleware.js +115 -0
  78. package/dist/middleware/require-platform-permission.middleware.d.ts +19 -0
  79. package/dist/middleware/require-platform-permission.middleware.js +91 -0
  80. package/dist/middleware/require-user.middleware.d.ts +14 -0
  81. package/dist/middleware/require-user.middleware.js +30 -0
  82. package/dist/middleware/require-workspace-permission.middleware.d.ts +20 -0
  83. package/dist/middleware/require-workspace-permission.middleware.js +114 -0
  84. package/dist/middleware/validate.middleware.d.ts +31 -31
  85. package/dist/middleware/validate.middleware.js +31 -33
  86. package/dist/types/audit.types.d.ts +39 -0
  87. package/dist/types/audit.types.js +30 -0
  88. package/dist/types/express.types.d.ts +92 -124
  89. package/dist/types/express.types.js +30 -49
  90. package/dist/types/index.d.ts +1 -1
  91. package/dist/utils/date.util.d.ts +6 -0
  92. package/dist/utils/date.util.js +11 -0
  93. package/dist/utils/env.util.d.ts +6 -0
  94. package/dist/utils/env.util.js +19 -0
  95. package/dist/utils/index.d.ts +5 -4
  96. package/dist/utils/index.js +5 -4
  97. package/dist/utils/logger.util.d.ts +31 -0
  98. package/dist/utils/logger.util.js +91 -0
  99. package/dist/utils/pagination.util.d.ts +6 -0
  100. package/dist/utils/pagination.util.js +20 -0
  101. package/dist/utils/request-context.als.d.ts +10 -0
  102. package/dist/utils/request-context.als.js +5 -0
  103. package/dist/utils/response.util.d.ts +27 -0
  104. package/dist/utils/response.util.js +56 -0
  105. package/dist/utils/slug.util.d.ts +9 -0
  106. package/dist/utils/slug.util.js +32 -0
  107. package/dist/utils/url-safety.util.d.ts +6 -0
  108. package/dist/utils/url-safety.util.js +75 -0
  109. package/package.json +3 -1
  110. package/dist/authorization/permissions.d.ts +0 -78
  111. package/dist/authorization/permissions.js +0 -174
  112. package/dist/context/access-context.d.ts +0 -10
  113. package/dist/context/access-context.js +0 -2
  114. package/dist/dto/auth-service.dtos.d.ts +0 -44
  115. package/dist/dto/auth-service.dtos.js +0 -2
  116. package/dist/dto/index.d.ts +0 -3
  117. package/dist/dto/response.dtos.d.ts +0 -55
  118. package/dist/dto/response.dtos.js +0 -6
  119. package/dist/dto/user-service.dtos.d.ts +0 -50
  120. package/dist/dto/user-service.dtos.js +0 -2
  121. package/dist/enums/auth-service.enums.d.ts +0 -12
  122. package/dist/enums/auth-service.enums.js +0 -17
  123. package/dist/enums/permissions.types.d.ts +0 -12
  124. package/dist/enums/permissions.types.js +0 -17
  125. package/dist/enums/user-service.enums.d.ts +0 -32
  126. package/dist/enums/user-service.enums.js +0 -41
  127. package/dist/internal/index.d.ts +0 -4
  128. package/dist/internal/index.js +0 -20
  129. package/dist/internal/internal-jwt.service.d.ts +0 -13
  130. package/dist/internal/internal-jwt.service.js +0 -88
  131. package/dist/internal/internal-jwt.types.d.ts +0 -7
  132. package/dist/internal/internal-jwt.types.js +0 -2
  133. package/dist/internal/internal-key-manager.d.ts +0 -16
  134. package/dist/internal/internal-key-manager.js +0 -67
  135. package/dist/internal/registry.d.ts +0 -8
  136. package/dist/internal/registry.js +0 -34
  137. package/dist/internal/service-client.d.ts +0 -9
  138. package/dist/internal/service-client.js +0 -94
  139. package/dist/jwt/internal-jwt-verifier.d.ts +0 -41
  140. package/dist/jwt/internal-jwt-verifier.js +0 -185
  141. package/dist/jwt/jwt-verifier.d.ts +0 -9
  142. package/dist/jwt/jwt-verifier.js +0 -36
  143. package/dist/jwt/service-client.d.ts +0 -7
  144. package/dist/jwt/service-client.js +0 -87
  145. package/dist/middleware/authorize.d.ts +0 -3
  146. package/dist/middleware/authorize.js +0 -24
  147. package/dist/middleware/authorize.middleware.d.ts +0 -54
  148. package/dist/middleware/authorize.middleware.js +0 -104
  149. package/dist/middleware/error-handler.d.ts +0 -4
  150. package/dist/middleware/error-handler.js +0 -23
  151. package/dist/middleware/request-id.d.ts +0 -2
  152. package/dist/middleware/request-id.js +0 -9
  153. package/dist/middleware/request-id.middleware.d.ts +0 -22
  154. package/dist/middleware/request-id.middleware.js +0 -34
  155. package/dist/middleware/require-auth.d.ts +0 -10
  156. package/dist/middleware/require-auth.js +0 -34
  157. package/dist/middleware/require-human.middleware.d.ts +0 -2
  158. package/dist/middleware/require-human.middleware.js +0 -18
  159. package/dist/middleware/require-internal.middleware.d.ts +0 -18
  160. package/dist/middleware/require-internal.middleware.js +0 -183
  161. package/dist/middleware/validate.d.ts +0 -5
  162. package/dist/middleware/validate.js +0 -18
  163. package/dist/middleware/validated-merge.middleware.d.ts +0 -20
  164. package/dist/middleware/validated-merge.middleware.js +0 -33
  165. package/dist/middleware/verify-internal-jwt.d.ts +0 -7
  166. package/dist/middleware/verify-internal-jwt.js +0 -25
  167. package/dist/security/guard.d.ts +0 -10
  168. package/dist/security/guard.js +0 -40
  169. package/dist/security/index.d.ts +0 -1
  170. package/dist/types/express.d.ts +0 -22
  171. package/dist/types/express.js +0 -3
  172. package/dist/utils/date.utils.d.ts +0 -25
  173. package/dist/utils/date.utils.js +0 -30
  174. package/dist/utils/env.d.ts +0 -46
  175. package/dist/utils/env.js +0 -61
  176. package/dist/utils/env.utils.d.ts +0 -46
  177. package/dist/utils/env.utils.js +0 -61
  178. package/dist/utils/logger.utils.d.ts +0 -66
  179. package/dist/utils/logger.utils.js +0 -97
  180. package/dist/utils/response.d.ts +0 -4
  181. package/dist/utils/response.js +0 -35
  182. package/dist/utils/response.utils.d.ts +0 -54
  183. package/dist/utils/response.utils.js +0 -85
@@ -1,185 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.InternalJwtVerifier = void 0;
37
- const jose = __importStar(require("jose"));
38
- /**
39
- * INTERNAL JWT VERIFIER (@discover-cloud/shared)
40
- * ──────────────────────────────────────────────────
41
- * Verifies the internal JWT minted by the API Gateway, resolves permissions
42
- * from Redis, and builds the AccessContext attached to req.accessContext.
43
- *
44
- * Key source:
45
- * Remote JWKS — fetched from the Gateway at startup and cached for 10 minutes.
46
- * Requests made during a JWKS cache miss are blocked until the fetch completes
47
- * (jose handles this internally with a deduplication lock).
48
- *
49
- * Token flow per request:
50
- * 1. Extract Bearer token from Authorization header (caller's responsibility)
51
- * 2. Verify JWT signature, iss, aud, exp, nbf, typ → verifyInternal()
52
- * 3. Guard typ === "internal" (token confusion protection)
53
- * 4. For human tokens: resolve perms from Redis (derive from role on miss)
54
- * 5. Build and return AccessContext → buildAccessContext()
55
- *
56
- * Token kinds:
57
- * - Human (isMachine: false) — carries accountId, accountRole, perms
58
- * - Machine (isMachine: true) — carries serviceId only; no perms
59
- *
60
- * Static helpers:
61
- * InternalJwtVerifier.hasPermission / requirePermission / hasRole / requireRole
62
- * are pure functions over AccessContext — safe to call anywhere after
63
- * auth middleware has run and populated req.accessContext.
64
- */
65
- const CLOCK_TOLERANCE_SECONDS = 30;
66
- const JWKS_CACHE_MAX_AGE_MS = 10 * 60 * 1000; // 10 minutes
67
- const JWKS_FETCH_TIMEOUT_MS = 5000;
68
- class InternalJwtVerifier {
69
- constructor(permissionCache) {
70
- this.permissionCache = permissionCache;
71
- const gatewayJwksUri = process.env["GATEWAY_JWKS_URI"] ?? "http://api-gateway:3000/.well-known/jwks.json";
72
- this.issuer = process.env["INTERNAL_JWT_ISSUER"] ?? "discover-cloud:api-gateway";
73
- this.audience = process.env["INTERNAL_JWT_AUDIENCE"] ?? "discover-cloud:internal";
74
- this.jwks = jose.createRemoteJWKSet(new URL(gatewayJwksUri), {
75
- cacheMaxAge: JWKS_CACHE_MAX_AGE_MS,
76
- timeoutDuration: JWKS_FETCH_TIMEOUT_MS,
77
- });
78
- }
79
- /* ----------------------------------------------------------------
80
- verifyInternal
81
- Full cryptographic verification + typ guard + clock skew tolerance.
82
- Returns the raw verified payload.
83
-
84
- Use this directly when you need raw JWT claims (e.g. jti for
85
- blacklisting). For all other cases, use buildAccessContext() which
86
- layers permission resolution on top.
87
- ---------------------------------------------------------------- */
88
- async verifyInternal(token) {
89
- const { payload } = await jose.jwtVerify(token, this.jwks, {
90
- issuer: this.issuer,
91
- audience: this.audience,
92
- algorithms: ["RS256"],
93
- clockTolerance: CLOCK_TOLERANCE_SECONDS,
94
- });
95
- // Explicit typ guard — rejects external tokens (e.g. user-facing access
96
- // tokens) that were accidentally forwarded past the gateway. This is a
97
- // token confusion defence: even if an external token passes signature
98
- // verification (same key pair), it will be rejected here.
99
- if (payload.typ !== "internal") {
100
- throw new jose.errors.JWTClaimValidationFailed(`Token type mismatch: expected "internal", got "${String(payload.typ)}". ` +
101
- `Ensure clients are not forwarding external tokens past the gateway.`, payload, "typ", "check_failed");
102
- }
103
- return payload;
104
- }
105
- /* ----------------------------------------------------------------
106
- buildAccessContext
107
- Primary method — called by RequireAuthMiddleware on every request.
108
- Verifies the token, resolves permissions, returns payload + context.
109
-
110
- Usage in auth middleware:
111
- const token = extractBearer(req);
112
- const { payload, context } = await verifier.buildAccessContext(token);
113
- req.internalAuth = payload;
114
- req.accessContext = context;
115
- ---------------------------------------------------------------- */
116
- async buildAccessContext(token) {
117
- const payload = await this.verifyInternal(token);
118
- // Machine token — no user context, no permission cache lookup.
119
- // Machine services are authorised by their serviceId + X-Internal-Secret,
120
- // not by role-based permissions.
121
- if (payload.isMachine === true) {
122
- const machinePayload = payload;
123
- const context = {
124
- kind: "machine",
125
- serviceId: machinePayload.serviceId,
126
- };
127
- return { payload, context };
128
- }
129
- // Human token — resolve permissions from Redis.
130
- // On a cache miss, permissions are derived from the static role map
131
- // (no DB call — the role is already verified in the JWT payload).
132
- const humanPayload = payload;
133
- const perms = await this.permissionCache.resolve(humanPayload.accountId, humanPayload.accountRole);
134
- const context = {
135
- kind: "human",
136
- accountId: humanPayload.accountId,
137
- accountRole: humanPayload.accountRole,
138
- perms, // always from cache — never read from the JWT directly
139
- };
140
- return { payload, context };
141
- }
142
- /* ----------------------------------------------------------------
143
- Static helpers — call on req.accessContext after auth middleware runs.
144
- These are pure functions; they do not touch Redis or the JWT verifier.
145
- ---------------------------------------------------------------- */
146
- /**
147
- * hasPermission
148
- * Returns true if the context carries the given GlobalPermission.
149
- * Machine contexts always return false — they carry no user permissions.
150
- */
151
- static hasPermission(ctx, permission) {
152
- return ctx.kind === "human" && ctx.perms.includes(permission);
153
- }
154
- /**
155
- * requirePermission
156
- * Throws JWTClaimValidationFailed if the permission is absent.
157
- * Use in middleware for a one-liner gate:
158
- * InternalJwtVerifier.requirePermission(ctx, GlobalPermission.MANAGE_ACCOUNTS);
159
- */
160
- static requirePermission(ctx, permission) {
161
- if (!InternalJwtVerifier.hasPermission(ctx, permission)) {
162
- throw new jose.errors.JWTClaimValidationFailed(`Missing required permission: ${permission}`, {}, "perms", "check_failed");
163
- }
164
- }
165
- /**
166
- * hasRole
167
- * Returns true if the context carries the given AccountRole.
168
- * Machine contexts always return false.
169
- */
170
- static hasRole(ctx, role) {
171
- return ctx.kind === "human" && ctx.accountRole === role;
172
- }
173
- /**
174
- * requireRole
175
- * Throws JWTClaimValidationFailed if the role doesn't match.
176
- * Prefer requirePermission() for most gates — role checks are for the rare
177
- * cases where a specific role (not just a permission) must be enforced.
178
- */
179
- static requireRole(ctx, role) {
180
- if (!InternalJwtVerifier.hasRole(ctx, role)) {
181
- throw new jose.errors.JWTClaimValidationFailed(`Missing required role: ${role}`, {}, "role", "check_failed");
182
- }
183
- }
184
- }
185
- exports.InternalJwtVerifier = InternalJwtVerifier;
@@ -1,9 +0,0 @@
1
- import { InternalJwtPayload } from "../types";
2
- export declare class JwtVerifier {
3
- private remoteJWKS;
4
- private getJWKS;
5
- /**
6
- * Verifies the Internal JWT sent by the Gateway.
7
- */
8
- verifyAccessToken(token: string): Promise<InternalJwtPayload>;
9
- }
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JwtVerifier = void 0;
4
- const jose_1 = require("jose");
5
- const errors_1 = require("../errors");
6
- class JwtVerifier {
7
- constructor() {
8
- this.remoteJWKS = null;
9
- }
10
- async getJWKS() {
11
- if (!this.remoteJWKS) {
12
- const base = process.env.API_GATEWAY_URL;
13
- this.remoteJWKS = (0, jose_1.createRemoteJWKSet)(new URL(`${base}/.well-known/jwks.json`));
14
- }
15
- return this.remoteJWKS;
16
- }
17
- /**
18
- * Verifies the Internal JWT sent by the Gateway.
19
- */
20
- async verifyAccessToken(token) {
21
- try {
22
- const JWKS = await this.getJWKS();
23
- const { payload } = await (0, jose_1.jwtVerify)(token, JWKS, {
24
- issuer: process.env.INTERNAL_JWT_ISSUER,
25
- audience: process.env.INTERNAL_JWT_AUDIENCE,
26
- });
27
- // Cast to our internal payload which includes the jti
28
- return payload;
29
- }
30
- catch (err) {
31
- console.error("[JwtVerifier] Internal Token Validation Failed:", err);
32
- throw new errors_1.UnauthorizedError("Invalid or expired internal token");
33
- }
34
- }
35
- }
36
- exports.JwtVerifier = JwtVerifier;
@@ -1,7 +0,0 @@
1
- import { AxiosInstance } from "axios";
2
- export declare class ServiceClient {
3
- readonly http: AxiosInstance;
4
- constructor();
5
- private setupRetry;
6
- private setupInterceptors;
7
- }
@@ -1,87 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.ServiceClient = void 0;
40
- const axios_1 = __importStar(require("axios"));
41
- const axios_retry_1 = __importDefault(require("axios-retry"));
42
- class ServiceClient {
43
- constructor() {
44
- this.http = axios_1.default.create({
45
- timeout: 8000,
46
- validateStatus: () => true
47
- });
48
- this.setupRetry();
49
- this.setupInterceptors();
50
- }
51
- setupRetry() {
52
- (0, axios_retry_1.default)(this.http, {
53
- retries: 3,
54
- retryDelay: axios_retry_1.default.exponentialDelay,
55
- retryCondition: (err) => {
56
- if (axios_retry_1.default.isNetworkError(err))
57
- return true;
58
- return (err.response?.status ?? 0) >= 500;
59
- }
60
- });
61
- }
62
- setupInterceptors() {
63
- this.http.interceptors.request.use(async (config) => {
64
- if (!config.headers)
65
- config.headers = new axios_1.AxiosHeaders();
66
- // Ensure every request has a tracing ID.
67
- // If the caller passed one down from the incoming Express request, use it.
68
- // Otherwise, generate a new one.
69
- const reqId = config.headers["x-request-id"] ||
70
- `req_${Math.random().toString(36).slice(2)}`;
71
- config.headers["x-request-id"] = reqId;
72
- return config;
73
- });
74
- this.http.interceptors.response.use((res) => {
75
- // Pass through successful responses cleanly
76
- if (res.status >= 200 && res.status < 300)
77
- return res;
78
- // Standardize error throwing for downstream microservice failures
79
- throw {
80
- status: res.status,
81
- message: res.data?.error?.message ?? "Downstream Service Error",
82
- details: res.data
83
- };
84
- });
85
- }
86
- }
87
- exports.ServiceClient = ServiceClient;
@@ -1,3 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- import { GlobalPermission } from "../enums";
3
- export declare const authorize: (permission: GlobalPermission) => (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
@@ -1,24 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.authorize = void 0;
4
- const security_1 = require("../security");
5
- const authorize = (permission) => {
6
- return (req, res, next) => {
7
- // 1. Ensure the RequireAuthMiddleware has already run
8
- if (!req.accessContext) {
9
- return res.status(401).json({
10
- success: false,
11
- error: { message: "Unauthorized: No access context", code: "UNAUTHORIZED" }
12
- });
13
- }
14
- // 2. Check if the Role in the accessContext has the required Permission
15
- if (!(0, security_1.isAllowed)(req.accessContext, permission)) {
16
- return res.status(403).json({
17
- success: false,
18
- error: { message: `Forbidden: Missing permission ${permission}`, code: "FORBIDDEN" }
19
- });
20
- }
21
- next();
22
- };
23
- };
24
- exports.authorize = authorize;
@@ -1,54 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- import { GlobalPermission } from "../enums";
3
- /**
4
- * AUTHORIZE MIDDLEWARE (@discover-cloud/shared)
5
- * ───────────────────────────────────────────────
6
- * Permission-gates a route. Must run AFTER requireAuth middleware, which
7
- * populates req.accessContext from the verified internal JWT + Redis cache.
8
- *
9
- * Uses InternalJwtVerifier.hasPermission — permissions come from the Redis
10
- * permission cache loaded by RequireAuthMiddleware, never from the JWT itself.
11
- *
12
- * All error responses go through the shared failure() util to ensure a
13
- * consistent ApiErrorResponse shape across every service. The error is
14
- * returned directly (not passed to next()) because these are deterministic
15
- * middleware guards — there is nothing to recover from downstream.
16
- *
17
- * Usage:
18
- * router.delete(
19
- * "/accounts/:id",
20
- * requireAuth,
21
- * authorize(GlobalPermission.DELETE_ACCOUNT),
22
- * controller.delete,
23
- * );
24
- */
25
- export declare const authorize: (permission: GlobalPermission) => (req: Request, res: Response, next: NextFunction) => void;
26
- /**
27
- * authorizeAny
28
- * Passes if the context has AT LEAST ONE of the provided permissions.
29
- * Useful for routes accessible by multiple roles (e.g. SUPPORT_VIEW or VIEW_ANY_ACCOUNT).
30
- *
31
- * Usage:
32
- * router.get(
33
- * "/accounts/:id",
34
- * requireAuth,
35
- * authorizeAny(GlobalPermission.VIEW_ANY_ACCOUNT, GlobalPermission.SUPPORT_VIEW),
36
- * controller.getById,
37
- * );
38
- */
39
- export declare const authorizeAny: (...permissions: GlobalPermission[]) => (req: Request, res: Response, next: NextFunction) => void;
40
- /**
41
- * authorizeAll
42
- * Passes only if the context has ALL of the provided permissions.
43
- * Use for routes that require a conjunction of permissions (rare — prefer
44
- * composing authorize() calls in series if the permissions are independent).
45
- *
46
- * Usage:
47
- * router.post(
48
- * "/accounts/:id/impersonate",
49
- * requireAuth,
50
- * authorizeAll(GlobalPermission.IMPERSONATE_ACCOUNT, GlobalPermission.MANAGE_ACCOUNTS),
51
- * controller.impersonate,
52
- * );
53
- */
54
- export declare const authorizeAll: (...permissions: GlobalPermission[]) => (req: Request, res: Response, next: NextFunction) => void;
@@ -1,104 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.authorizeAll = exports.authorizeAny = exports.authorize = void 0;
4
- const internal_jwt_verifier_1 = require("../jwt/internal-jwt-verifier");
5
- const utils_1 = require("../utils");
6
- /**
7
- * AUTHORIZE MIDDLEWARE (@discover-cloud/shared)
8
- * ───────────────────────────────────────────────
9
- * Permission-gates a route. Must run AFTER requireAuth middleware, which
10
- * populates req.accessContext from the verified internal JWT + Redis cache.
11
- *
12
- * Uses InternalJwtVerifier.hasPermission — permissions come from the Redis
13
- * permission cache loaded by RequireAuthMiddleware, never from the JWT itself.
14
- *
15
- * All error responses go through the shared failure() util to ensure a
16
- * consistent ApiErrorResponse shape across every service. The error is
17
- * returned directly (not passed to next()) because these are deterministic
18
- * middleware guards — there is nothing to recover from downstream.
19
- *
20
- * Usage:
21
- * router.delete(
22
- * "/accounts/:id",
23
- * requireAuth,
24
- * authorize(GlobalPermission.DELETE_ACCOUNT),
25
- * controller.delete,
26
- * );
27
- */
28
- const authorize = (permission) => {
29
- return (req, res, next) => {
30
- // Ensure RequireAuthMiddleware has already run and populated accessContext.
31
- // This is a programming error (wrong middleware order), not a client error,
32
- // but we surface it as 401 so it doesn't silently pass through.
33
- if (!req.accessContext) {
34
- (0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
35
- return;
36
- }
37
- // Check permission against accessContext.perms (populated from Redis cache).
38
- if (!internal_jwt_verifier_1.InternalJwtVerifier.hasPermission(req.accessContext, permission)) {
39
- (0, utils_1.failure)(res, req, `Forbidden: Missing permission ${permission}`, "FORBIDDEN", 403);
40
- return;
41
- }
42
- next();
43
- };
44
- };
45
- exports.authorize = authorize;
46
- /**
47
- * authorizeAny
48
- * Passes if the context has AT LEAST ONE of the provided permissions.
49
- * Useful for routes accessible by multiple roles (e.g. SUPPORT_VIEW or VIEW_ANY_ACCOUNT).
50
- *
51
- * Usage:
52
- * router.get(
53
- * "/accounts/:id",
54
- * requireAuth,
55
- * authorizeAny(GlobalPermission.VIEW_ANY_ACCOUNT, GlobalPermission.SUPPORT_VIEW),
56
- * controller.getById,
57
- * );
58
- */
59
- const authorizeAny = (...permissions) => {
60
- return (req, res, next) => {
61
- const ctx = req.accessContext;
62
- if (!ctx) {
63
- (0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
64
- return;
65
- }
66
- const hasAny = permissions.some((p) => internal_jwt_verifier_1.InternalJwtVerifier.hasPermission(ctx, p));
67
- if (!hasAny) {
68
- (0, utils_1.failure)(res, req, `Forbidden: Missing one of [${permissions.join(", ")}]`, "FORBIDDEN", 403);
69
- return;
70
- }
71
- next();
72
- };
73
- };
74
- exports.authorizeAny = authorizeAny;
75
- /**
76
- * authorizeAll
77
- * Passes only if the context has ALL of the provided permissions.
78
- * Use for routes that require a conjunction of permissions (rare — prefer
79
- * composing authorize() calls in series if the permissions are independent).
80
- *
81
- * Usage:
82
- * router.post(
83
- * "/accounts/:id/impersonate",
84
- * requireAuth,
85
- * authorizeAll(GlobalPermission.IMPERSONATE_ACCOUNT, GlobalPermission.MANAGE_ACCOUNTS),
86
- * controller.impersonate,
87
- * );
88
- */
89
- const authorizeAll = (...permissions) => {
90
- return (req, res, next) => {
91
- const ctx = req.accessContext;
92
- if (!ctx) {
93
- (0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
94
- return;
95
- }
96
- const missingPermissions = permissions.filter((p) => !internal_jwt_verifier_1.InternalJwtVerifier.hasPermission(ctx, p));
97
- if (missingPermissions.length > 0) {
98
- (0, utils_1.failure)(res, req, `Forbidden: Missing permissions [${missingPermissions.join(", ")}]`, "FORBIDDEN", 403);
99
- return;
100
- }
101
- next();
102
- };
103
- };
104
- exports.authorizeAll = authorizeAll;
@@ -1,4 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- export declare class GlobalErrorHandler {
3
- static handle(err: unknown, req: Request, res: Response, _next: NextFunction): Response<any, Record<string, any>>;
4
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GlobalErrorHandler = void 0;
4
- const zod_1 = require("zod");
5
- const utils_1 = require("../utils");
6
- class GlobalErrorHandler {
7
- static handle(err, req, res, _next) {
8
- // Standardized logging using req.id from your requestId middleware
9
- console.error(`[req ${req.id}]`, err);
10
- // 1. Zod Validation Errors
11
- if (err instanceof zod_1.ZodError) {
12
- return (0, utils_1.failure)(res, "Validation failed", 400, err.flatten());
13
- }
14
- // 2. Custom App Errors (instanceof checks for BadRequestError, etc.)
15
- const custom = err;
16
- if (custom.statusCode) {
17
- return (0, utils_1.failure)(res, custom.message ?? "Error", custom.statusCode, custom.details);
18
- }
19
- // 3. Fallback: Internal Server Error
20
- return (0, utils_1.failure)(res, "Internal Server Error", 500);
21
- }
22
- }
23
- exports.GlobalErrorHandler = GlobalErrorHandler;
@@ -1,2 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- export declare const requestId: (req: Request, _res: Response, next: NextFunction) => void;
@@ -1,9 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.requestId = void 0;
4
- const crypto_1 = require("crypto");
5
- const requestId = (req, _res, next) => {
6
- req.id = (0, crypto_1.randomUUID)();
7
- next();
8
- };
9
- exports.requestId = requestId;
@@ -1,22 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- /**
3
- * REQUEST ID MIDDLEWARE (@discover-cloud/shared)
4
- * ─────────────────────────────────────────────────
5
- * Attaches a unique request ID to req.id for distributed tracing.
6
- * Register early in the middleware stack — before any logger or handler
7
- * that needs to include the request ID in its output.
8
- *
9
- * Priority:
10
- * 1. x-request-id header forwarded by the API Gateway (or load balancer)
11
- * 2. Newly generated UUID v4 if no upstream header is present
12
- *
13
- * Why forward the upstream header?
14
- * The gateway mints a requestId and stamps it on every internal request.
15
- * If downstream services generate their own IDs, the same logical request
16
- * carries different IDs in each service's logs, breaking cross-service
17
- * log correlation. Forwarding the upstream ID keeps the trace consistent.
18
- *
19
- * The resolved ID is also echoed back on the response via x-request-id
20
- * so clients and proxies can correlate their own logs with the service's.
21
- */
22
- export declare const requestId: (req: Request, res: Response, next: NextFunction) => void;
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.requestId = void 0;
4
- const crypto_1 = require("crypto");
5
- /**
6
- * REQUEST ID MIDDLEWARE (@discover-cloud/shared)
7
- * ─────────────────────────────────────────────────
8
- * Attaches a unique request ID to req.id for distributed tracing.
9
- * Register early in the middleware stack — before any logger or handler
10
- * that needs to include the request ID in its output.
11
- *
12
- * Priority:
13
- * 1. x-request-id header forwarded by the API Gateway (or load balancer)
14
- * 2. Newly generated UUID v4 if no upstream header is present
15
- *
16
- * Why forward the upstream header?
17
- * The gateway mints a requestId and stamps it on every internal request.
18
- * If downstream services generate their own IDs, the same logical request
19
- * carries different IDs in each service's logs, breaking cross-service
20
- * log correlation. Forwarding the upstream ID keeps the trace consistent.
21
- *
22
- * The resolved ID is also echoed back on the response via x-request-id
23
- * so clients and proxies can correlate their own logs with the service's.
24
- */
25
- const requestId = (req, res, next) => {
26
- const upstream = req.headers["x-request-id"];
27
- // Header can be a string array (multi-value header) — take the first value.
28
- const id = Array.isArray(upstream) ? upstream[0] : (upstream ?? (0, crypto_1.randomUUID)());
29
- req.id = id;
30
- // Echo back on the response for client-side and proxy-side correlation.
31
- res.setHeader("x-request-id", id);
32
- next();
33
- };
34
- exports.requestId = requestId;
@@ -1,10 +0,0 @@
1
- import { Request, Response, NextFunction } from "express";
2
- import { InternalJwtPayload } from "../types";
3
- export interface IJwtVerifier {
4
- verifyAccessToken(token: string): Promise<InternalJwtPayload>;
5
- }
6
- export declare class RequireAuthMiddleware {
7
- private readonly verifier;
8
- constructor(verifier: IJwtVerifier);
9
- handle: (req: Request, _res: Response, next: NextFunction) => Promise<void>;
10
- }
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RequireAuthMiddleware = void 0;
4
- const errors_1 = require("../errors");
5
- class RequireAuthMiddleware {
6
- constructor(verifier) {
7
- this.verifier = verifier;
8
- this.handle = async (req, _res, next) => {
9
- const header = req.headers.authorization;
10
- if (!header?.startsWith("Bearer ")) {
11
- return next(new errors_1.UnauthorizedError("Missing Authorization header"));
12
- }
13
- const token = header.slice(7);
14
- try {
15
- // 1. Verify against Gateway Public Keys
16
- const payload = await this.verifier.verifyAccessToken(token);
17
- // 2. Store the raw payload (Access to .jti for logouts/suspensions)
18
- req.internalAuth = payload;
19
- // 3. Build AccessContext if it's a human user
20
- if (payload.accountId && payload.accountRole) {
21
- req.accessContext = {
22
- accountId: payload.accountId,
23
- accountRole: payload.accountRole,
24
- };
25
- }
26
- next();
27
- }
28
- catch (err) {
29
- next(new errors_1.UnauthorizedError("Unauthorized: Token validation failed"));
30
- }
31
- };
32
- }
33
- }
34
- exports.RequireAuthMiddleware = RequireAuthMiddleware;