@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,85 +1,16 @@
1
- import { Request, Response, NextFunction } from "express";
2
- import { AccountRole, GlobalPermission } from "../enums";
3
- import { ILogger } from "../utils";
4
- import { InternalJwtVerifier } from "../jwt/internal-jwt-verifier";
5
1
  /**
6
- * REQUIRE AUTH MIDDLEWARE (@discover-cloud/shared)
7
- * ─────────────────────────────────────────────────────────────────────────
8
- * Verifies the internal JWT, resolves permissions from the Redis cache,
9
- * and attaches the typed AccessContext to req.accessContext.
10
- *
11
- * Moved to shared so every downstream service (auth, user, cloud, insights)
12
- * uses the same verified implementation rather than maintaining their own copy.
13
- *
14
- * Token extraction:
15
- * Reads a Bearer token from the Authorization header.
16
- * Does not support cookie-based auth — that is the API gateway's responsibility.
17
- *
18
- * On success:
19
- * req.internalAuth → raw verified JWT payload (use for jti, caller, requestId)
20
- * req.accessContext → clean typed context (use in controllers and services)
21
- *
22
- * On failure:
23
- * Returns a structured error response and does NOT call next().
24
- * next(err) is only called for genuinely unexpected errors (not auth failures).
25
- *
26
- * Inline guards (requirePermission, requireRole):
27
- * Convenience methods for single-route guards. For router-level permission
28
- * enforcement, prefer the standalone factories in permission.middleware.ts —
29
- * they don't require a reference to this class instance.
30
- *
31
- * Logging:
32
- * Unexpected errors are logged at "error" level with requestId for correlation.
33
- * Auth failures (expired, invalid token) are intentionally not logged at error
34
- * level — they are client errors, not server faults.
35
- *
36
- * Usage in each service's app.ts:
37
- * import { RequireAuthMiddleware } from "@discover-cloud/shared";
38
- *
39
- * const requireAuth = new RequireAuthMiddleware(jwtVerifier, logger);
40
- * router.use(requireAuth.handle);
2
+ * @file require-auth.middleware.ts
3
+ * @description Authentication boundary middleware. Verifies the internal gateway JWT,
4
+ * builds the auth context, and gates endpoints.
41
5
  */
6
+ import { Request, Response, NextFunction } from 'express';
7
+ import { ILogger } from '../utils';
8
+ import { UserJwtVerifier } from '../jwt/user-jwt-verifier';
42
9
  export declare class RequireAuthMiddleware {
43
10
  private readonly verifier;
44
11
  private readonly logger;
45
- constructor(verifier: InternalJwtVerifier, logger?: ILogger);
46
- /**
47
- * Core authentication middleware.
48
- * Apply via: router.use(requireAuth.handle) or router.get("/", requireAuth.handle, controller.x)
49
- */
12
+ constructor(verifier: UserJwtVerifier, logger?: ILogger);
50
13
  handle: (req: Request, res: Response, next: NextFunction) => Promise<void>;
51
- /**
52
- * Inline permission guard — use when this class instance is available at the call site.
53
- * For router-level guards, prefer requireGlobalPermission() from permission.middleware.ts.
54
- *
55
- * Usage: router.get("/", requireAuth.handle, requireAuth.requirePermission(P.READ_ACCOUNTS), controller.list)
56
- */
57
- requirePermission: (permission: GlobalPermission) => (req: Request, res: Response, next: NextFunction) => void;
58
- /**
59
- * Inline role guard — use when this class instance is available at the call site.
60
- *
61
- * Prefer permission-based guards over role-based guards where possible.
62
- * Role checks are appropriate for coarse access gates (e.g. "only admins enter this router").
63
- */
64
- requireRole: (role: AccountRole) => (req: Request, res: Response, next: NextFunction) => void;
65
- /**
66
- * Extracts the Bearer token from the Authorization header.
67
- * Returns null (not an error) if the header is absent or malformed —
68
- * callers decide how to handle the missing token.
69
- */
70
14
  private extractBearer;
71
- /**
72
- * Maps jose verification errors to appropriate HTTP responses.
73
- *
74
- * Error categories:
75
- * JWTExpired → 401 TOKEN_EXPIRED (client should refresh)
76
- * JWTClaimValidationFailed,
77
- * JWSSignatureVerificationFailed,
78
- * JWSInvalid, JWTInvalid → 401 INVALID_TOKEN (client should re-login)
79
- * Anything else → 503 SERVICE_UNAVAILABLE (unexpected — log at error)
80
- *
81
- * Security note: generic "invalid token" messages are intentional.
82
- * Never expose which specific claim failed — that aids token forgery attempts.
83
- */
84
15
  private handleJwtError;
85
16
  }
@@ -1,162 +1,74 @@
1
1
  "use strict";
2
+ /**
3
+ * @file require-auth.middleware.ts
4
+ * @description Authentication boundary middleware. Verifies the internal gateway JWT,
5
+ * builds the auth context, and gates endpoints.
6
+ */
2
7
  Object.defineProperty(exports, "__esModule", { value: true });
3
8
  exports.RequireAuthMiddleware = void 0;
4
9
  const jose_1 = require("jose");
5
10
  const utils_1 = require("../utils");
6
- const internal_jwt_verifier_1 = require("../jwt/internal-jwt-verifier");
7
- /**
8
- * REQUIRE AUTH MIDDLEWARE (@discover-cloud/shared)
9
- * ─────────────────────────────────────────────────────────────────────────
10
- * Verifies the internal JWT, resolves permissions from the Redis cache,
11
- * and attaches the typed AccessContext to req.accessContext.
12
- *
13
- * Moved to shared so every downstream service (auth, user, cloud, insights)
14
- * uses the same verified implementation rather than maintaining their own copy.
15
- *
16
- * Token extraction:
17
- * Reads a Bearer token from the Authorization header.
18
- * Does not support cookie-based auth — that is the API gateway's responsibility.
19
- *
20
- * On success:
21
- * req.internalAuth → raw verified JWT payload (use for jti, caller, requestId)
22
- * req.accessContext → clean typed context (use in controllers and services)
23
- *
24
- * On failure:
25
- * Returns a structured error response and does NOT call next().
26
- * next(err) is only called for genuinely unexpected errors (not auth failures).
27
- *
28
- * Inline guards (requirePermission, requireRole):
29
- * Convenience methods for single-route guards. For router-level permission
30
- * enforcement, prefer the standalone factories in permission.middleware.ts —
31
- * they don't require a reference to this class instance.
32
- *
33
- * Logging:
34
- * Unexpected errors are logged at "error" level with requestId for correlation.
35
- * Auth failures (expired, invalid token) are intentionally not logged at error
36
- * level — they are client errors, not server faults.
37
- *
38
- * Usage in each service's app.ts:
39
- * import { RequireAuthMiddleware } from "@discover-cloud/shared";
40
- *
41
- * const requireAuth = new RequireAuthMiddleware(jwtVerifier, logger);
42
- * router.use(requireAuth.handle);
43
- */
11
+ const request_context_storage_1 = require("../context/request-context.storage");
44
12
  class RequireAuthMiddleware {
45
13
  constructor(verifier, logger = utils_1.noopLogger) {
46
14
  this.verifier = verifier;
47
15
  this.logger = logger;
48
- /**
49
- * Core authentication middleware.
50
- * Apply via: router.use(requireAuth.handle) or router.get("/", requireAuth.handle, controller.x)
51
- */
52
16
  this.handle = async (req, res, next) => {
53
17
  const token = this.extractBearer(req);
54
18
  if (!token) {
55
- (0, utils_1.failure)(res, req, "Missing Authorization header", "UNAUTHORIZED", 401);
19
+ (0, utils_1.failure)(res, req, 'Missing Authorization header', 'UNAUTHORIZED', 401);
56
20
  return;
57
21
  }
58
22
  try {
59
23
  const { payload, context } = await this.verifier.buildAccessContext(token);
60
- req.internalAuth = payload;
61
- req.accessContext = context;
24
+ req.authPayload = payload;
25
+ req.authContext = context;
26
+ // Attach audit context. Will throw if requestContextStorage is not initialized.
27
+ request_context_storage_1.RequestContextProvider.setActor(context);
62
28
  this.logger.debug({
63
29
  requestId: req.id,
64
- // log accountId for humans, serviceId for machines — never log both
65
- identity: context.kind === "human" ? context.accountId : context.serviceId,
66
- kind: context.kind,
67
- caller: payload.caller,
68
- }, "Request authenticated");
30
+ identity: context.accountId,
31
+ type: context.type,
32
+ }, 'Request authenticated');
69
33
  next();
70
34
  }
71
35
  catch (err) {
72
36
  this.handleJwtError(err, req, res);
73
37
  }
74
38
  };
75
- /**
76
- * Inline permission guard — use when this class instance is available at the call site.
77
- * For router-level guards, prefer requireGlobalPermission() from permission.middleware.ts.
78
- *
79
- * Usage: router.get("/", requireAuth.handle, requireAuth.requirePermission(P.READ_ACCOUNTS), controller.list)
80
- */
81
- this.requirePermission = (permission) => (req, res, next) => {
82
- const ctx = req.accessContext;
83
- if (!ctx) {
84
- // Should not happen after handle() — indicates middleware ordering bug
85
- (0, utils_1.failure)(res, req, "No access context — ensure requireAuth runs first", "UNAUTHORIZED", 401);
86
- return;
87
- }
88
- if (!internal_jwt_verifier_1.InternalJwtVerifier.hasPermission(ctx, permission)) {
89
- (0, utils_1.failure)(res, req, `Missing required permission: ${permission}`, "FORBIDDEN", 403);
90
- return;
91
- }
92
- next();
93
- };
94
- /**
95
- * Inline role guard — use when this class instance is available at the call site.
96
- *
97
- * Prefer permission-based guards over role-based guards where possible.
98
- * Role checks are appropriate for coarse access gates (e.g. "only admins enter this router").
99
- */
100
- this.requireRole = (role) => (req, res, next) => {
101
- const ctx = req.accessContext;
102
- if (!ctx) {
103
- (0, utils_1.failure)(res, req, "No access context — ensure requireAuth runs first", "UNAUTHORIZED", 401);
104
- return;
105
- }
106
- if (!internal_jwt_verifier_1.InternalJwtVerifier.hasRole(ctx, role)) {
107
- (0, utils_1.failure)(res, req, `Missing required role: ${role}`, "FORBIDDEN", 403);
108
- return;
109
- }
110
- next();
111
- };
112
39
  }
113
- /* ────────────────────────────────────────────────────────────────────
114
- PRIVATE HELPERS
115
- ──────────────────────────────────────────────────────────────────── */
116
- /**
117
- * Extracts the Bearer token from the Authorization header.
118
- * Returns null (not an error) if the header is absent or malformed —
119
- * callers decide how to handle the missing token.
120
- */
40
+ /* ---------------------------------------------------------------------------
41
+ Private Helpers
42
+ --------------------------------------------------------------------------- */
121
43
  extractBearer(req) {
122
44
  const header = req.headers.authorization;
123
- if (!header) {
45
+ if (!header)
124
46
  return null;
125
- }
126
47
  const [scheme, token] = header.trim().split(/\s+/);
127
- if (scheme !== "Bearer" || !token) {
48
+ if (scheme !== 'Bearer' || !token)
128
49
  return null;
129
- }
130
50
  return token;
131
51
  }
132
- /**
133
- * Maps jose verification errors to appropriate HTTP responses.
134
- *
135
- * Error categories:
136
- * JWTExpired → 401 TOKEN_EXPIRED (client should refresh)
137
- * JWTClaimValidationFailed,
138
- * JWSSignatureVerificationFailed,
139
- * JWSInvalid, JWTInvalid → 401 INVALID_TOKEN (client should re-login)
140
- * Anything else → 503 SERVICE_UNAVAILABLE (unexpected — log at error)
141
- *
142
- * Security note: generic "invalid token" messages are intentional.
143
- * Never expose which specific claim failed — that aids token forgery attempts.
144
- */
145
52
  handleJwtError(err, req, res) {
146
53
  if (err instanceof jose_1.errors.JWTExpired) {
147
- (0, utils_1.failure)(res, req, "Your session has expired. Please log in again.", "TOKEN_EXPIRED", 401);
54
+ (0, utils_1.failure)(res, req, 'Your session has expired. Please log in again.', 'TOKEN_EXPIRED', 401);
148
55
  return;
149
56
  }
150
57
  if (err instanceof jose_1.errors.JWTClaimValidationFailed ||
151
58
  err instanceof jose_1.errors.JWSSignatureVerificationFailed ||
152
59
  err instanceof jose_1.errors.JWSInvalid ||
153
60
  err instanceof jose_1.errors.JWTInvalid) {
154
- (0, utils_1.failure)(res, req, "Token is invalid or has been tampered with.", "INVALID_TOKEN", 401);
61
+ (0, utils_1.failure)(res, req, 'Token is invalid or has been tampered with.', 'INVALID_TOKEN', 401);
62
+ return;
63
+ }
64
+ // Catch the specific Developer Error from RequestContextProvider
65
+ if (err instanceof Error && err.message.includes('AuditRequestContext')) {
66
+ this.logger.error({ err, requestId: req.id }, 'CRITICAL: RequireAuthMiddleware failed because requestContextStorage is not initialized. Check your middleware order.');
67
+ (0, utils_1.failure)(res, req, 'Internal server configuration error.', 'INTERNAL_SERVER_ERROR', 500);
155
68
  return;
156
69
  }
157
- // Unexpected error log for investigation, return a safe 503
158
- this.logger.error({ err, requestId: req.id }, "RequireAuthMiddleware: unexpected error during JWT verification");
159
- (0, utils_1.failure)(res, req, "Authentication service is temporarily unavailable.", "SERVICE_UNAVAILABLE", 503);
70
+ this.logger.error({ err, requestId: req.id }, 'RequireAuthMiddleware: unexpected error during JWT verification');
71
+ (0, utils_1.failure)(res, req, 'Authentication service is temporarily unavailable.', 'SERVICE_UNAVAILABLE', 503);
160
72
  }
161
73
  }
162
74
  exports.RequireAuthMiddleware = RequireAuthMiddleware;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @file require-machine.middleware.ts
3
+ * @description Express middleware enforcing auth-service issued machine JWT verification
4
+ * for service-to-service communication endpoints.
5
+ */
6
+ import { RequestHandler } from 'express';
7
+ import { ILogger } from '../utils/logger.util';
8
+ export interface RequireMachineOptions {
9
+ authServiceJwksUri?: string;
10
+ allowedServices?: string[];
11
+ logger?: ILogger;
12
+ }
13
+ export declare function requireMachine(options?: RequireMachineOptions): RequestHandler;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * @file require-machine.middleware.ts
4
+ * @description Express middleware enforcing auth-service issued machine JWT verification
5
+ * for service-to-service communication endpoints.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.requireMachine = requireMachine;
42
+ const jose = __importStar(require("jose"));
43
+ const logger_util_1 = require("../utils/logger.util");
44
+ const machine_jwt_verifier_1 = require("../jwt/machine-jwt-verifier");
45
+ const request_context_storage_1 = require("../context/request-context.storage");
46
+ function requireMachine(options = {}) {
47
+ const logger = options.logger ?? logger_util_1.noopLogger;
48
+ const verifier = new machine_jwt_verifier_1.MachineJwtVerifier(options.authServiceJwksUri);
49
+ const { allowedServices } = options;
50
+ return async (req, res, next) => {
51
+ const header = req.headers.authorization;
52
+ if (!header?.startsWith('Bearer ')) {
53
+ res.status(401).json({
54
+ success: false,
55
+ error: {
56
+ code: 'UNAUTHORIZED',
57
+ message: 'Missing Authorization header',
58
+ },
59
+ });
60
+ return;
61
+ }
62
+ const token = header.slice(7);
63
+ try {
64
+ const payload = await verifier.verify(token);
65
+ if (allowedServices && !allowedServices.includes(payload.serviceId)) {
66
+ logger.warn({ requestId: req.id, serviceId: payload.serviceId }, '[requireMachine] Service rejected');
67
+ res.status(403).json({
68
+ success: false,
69
+ error: {
70
+ code: 'FORBIDDEN',
71
+ message: 'Service is not permitted to access this endpoint',
72
+ },
73
+ });
74
+ return;
75
+ }
76
+ const context = {
77
+ type: 'machine',
78
+ serviceId: payload.serviceId,
79
+ scopes: payload.scopes,
80
+ };
81
+ req.authPayload = payload;
82
+ req.authContext = context;
83
+ // Attach audit context. Will throw if requestContextStorage is not initialized.
84
+ request_context_storage_1.RequestContextProvider.setActor(context);
85
+ logger.debug({ requestId: req.id, serviceId: payload.serviceId, jti: payload.jti }, '[requireMachine] Machine authenticated');
86
+ next();
87
+ }
88
+ catch (err) {
89
+ if (err instanceof jose.errors.JWTExpired) {
90
+ res.status(401).json({
91
+ success: false,
92
+ error: { code: 'TOKEN_EXPIRED', message: 'Machine token expired' },
93
+ });
94
+ return;
95
+ }
96
+ if (err instanceof jose.errors.JWTClaimValidationFailed ||
97
+ err instanceof jose.errors.JWSSignatureVerificationFailed ||
98
+ err instanceof jose.errors.JWSInvalid ||
99
+ err instanceof jose.errors.JWTInvalid) {
100
+ res.status(401).json({
101
+ success: false,
102
+ error: { code: 'INVALID_TOKEN', message: 'Invalid machine token' },
103
+ });
104
+ return;
105
+ }
106
+ // Catch the specific Developer Error from RequestContextProvider
107
+ if (err instanceof Error && err.message.includes('AuditRequestContext')) {
108
+ logger.error({ err, requestId: req.id }, 'CRITICAL: [requireMachine] failed because requestContextStorage is not initialized. Check middleware order.');
109
+ res.status(500).json({
110
+ success: false,
111
+ error: {
112
+ code: 'INTERNAL_SERVER_ERROR',
113
+ message: 'Internal server configuration error',
114
+ },
115
+ });
116
+ return;
117
+ }
118
+ logger.error({ err, requestId: req.id }, '[requireMachine] Machine authentication failure');
119
+ res.status(503).json({
120
+ success: false,
121
+ error: {
122
+ code: 'SERVICE_UNAVAILABLE',
123
+ message: 'Machine authentication temporarily unavailable',
124
+ },
125
+ });
126
+ }
127
+ };
128
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file require-org-permission-from-body.middleware.ts
3
+ * @description Checks org permission only when organizationId exists in request body.
4
+ * Used for endpoints that support both personal and organization scopes.
5
+ */
6
+ import { RequestHandler } from 'express';
7
+ import { Permission } from '../enums';
8
+ export declare const createRequireOrgPermissionFromBody: (requireOrgPermission: (permission: Permission) => RequestHandler) => {
9
+ requireOrgPermissionFromBody: (permission: Permission) => RequestHandler;
10
+ };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /**
3
+ * @file require-org-permission-from-body.middleware.ts
4
+ * @description Checks org permission only when organizationId exists in request body.
5
+ * Used for endpoints that support both personal and organization scopes.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createRequireOrgPermissionFromBody = void 0;
9
+ const createRequireOrgPermissionFromBody = (requireOrgPermission) => {
10
+ const requireOrgPermissionFromBody = (permission) => {
11
+ return async (req, res, next) => {
12
+ const organizationId = req.body?.organizationId ?? req.validated?.body?.organizationId;
13
+ if (!organizationId) {
14
+ return next();
15
+ }
16
+ return requireOrgPermission(permission)(req, res, next);
17
+ };
18
+ };
19
+ return {
20
+ requireOrgPermissionFromBody,
21
+ };
22
+ };
23
+ exports.createRequireOrgPermissionFromBody = createRequireOrgPermissionFromBody;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @file require-org-permission.middleware.ts
3
+ * @description Express middleware generator to enforce organization-scoped permission checks.
4
+ */
5
+ import { RequestHandler } from 'express';
6
+ import { Permission } from '../enums';
7
+ import { PermissionCacheService } from '../authorization/permission-cache.service';
8
+ /**
9
+ * Creates requireOrgPermission middleware factory.
10
+ *
11
+ * @param {PermissionCacheService} permissionCache - Shared Redis cache service.
12
+ * @param {(accountId: string, organizationId: string) => Promise<string[]>} resolveFn - Cache miss database/API lookup function.
13
+ * @returns {object} Organization permission middlewares.
14
+ */
15
+ export declare const createRequireOrgPermission: (permissionCache: PermissionCacheService, resolveFn: (accountId: string, organizationId: string) => Promise<string[]>) => {
16
+ requireOrgPermission: (permission: Permission) => RequestHandler;
17
+ requireAnyOrgPermission: (...permissions: Permission[]) => RequestHandler;
18
+ requireAllOrgPermissions: (...permissions: Permission[]) => RequestHandler;
19
+ };
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * @file require-org-permission.middleware.ts
4
+ * @description Express middleware generator to enforce organization-scoped permission checks.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createRequireOrgPermission = void 0;
8
+ const permission_checker_1 = require("../authorization/permission-checker");
9
+ const utils_1 = require("../utils");
10
+ /**
11
+ * Creates requireOrgPermission middleware factory.
12
+ *
13
+ * @param {PermissionCacheService} permissionCache - Shared Redis cache service.
14
+ * @param {(accountId: string, organizationId: string) => Promise<string[]>} resolveFn - Cache miss database/API lookup function.
15
+ * @returns {object} Organization permission middlewares.
16
+ */
17
+ const createRequireOrgPermission = (permissionCache, resolveFn) => {
18
+ const requireOrgPermission = (permission) => {
19
+ return async (req, res, next) => {
20
+ const ctx = req.authContext;
21
+ if (!ctx || ctx.type !== 'user') {
22
+ (0, utils_1.failure)(res, req, 'Unauthorized: No auth context', 'UNAUTHORIZED', 401);
23
+ return;
24
+ }
25
+ const organizationId = req.params['organizationId'] ??
26
+ req.params['orgId'] ??
27
+ req.validated?.params?.['organizationId'] ??
28
+ req.validated?.body?.['organizationId'];
29
+ if (!organizationId || typeof organizationId !== 'string') {
30
+ (0, utils_1.failure)(res, req, 'Bad Request: Missing organization context', 'BAD_REQUEST', 400);
31
+ return;
32
+ }
33
+ try {
34
+ const perms = await permissionCache.resolveOrganizationPermissions(ctx.accountId, organizationId, async () => {
35
+ return resolveFn(ctx.accountId, organizationId);
36
+ });
37
+ if (!permission_checker_1.PermissionChecker.has(perms, permission)) {
38
+ (0, utils_1.failure)(res, req, `Forbidden: Missing required organization permission: ${permission}`, 'FORBIDDEN', 403);
39
+ return;
40
+ }
41
+ next();
42
+ }
43
+ catch (err) {
44
+ next(err);
45
+ }
46
+ };
47
+ };
48
+ const requireAnyOrgPermission = (...permissions) => {
49
+ return async (req, res, next) => {
50
+ const ctx = req.authContext;
51
+ if (!ctx || ctx.type !== 'user') {
52
+ (0, utils_1.failure)(res, req, 'Unauthorized: No auth context', 'UNAUTHORIZED', 401);
53
+ return;
54
+ }
55
+ const organizationId = req.params['organizationId'] ??
56
+ req.params['orgId'] ??
57
+ req.validated?.params?.['organizationId'] ??
58
+ req.validated?.body?.['organizationId'];
59
+ if (!organizationId || typeof organizationId !== 'string') {
60
+ (0, utils_1.failure)(res, req, 'Bad Request: Missing organization context', 'BAD_REQUEST', 400);
61
+ return;
62
+ }
63
+ try {
64
+ const perms = await permissionCache.resolveOrganizationPermissions(ctx.accountId, organizationId, async () => {
65
+ return resolveFn(ctx.accountId, organizationId);
66
+ });
67
+ if (!permission_checker_1.PermissionChecker.hasAny(perms, permissions)) {
68
+ (0, utils_1.failure)(res, req, `Forbidden: Missing one of required organization permissions: [${permissions.join(', ')}]`, 'FORBIDDEN', 403);
69
+ return;
70
+ }
71
+ next();
72
+ }
73
+ catch (err) {
74
+ next(err);
75
+ }
76
+ };
77
+ };
78
+ const requireAllOrgPermissions = (...permissions) => {
79
+ return async (req, res, next) => {
80
+ const ctx = req.authContext;
81
+ if (!ctx || ctx.type !== 'user') {
82
+ (0, utils_1.failure)(res, req, 'Unauthorized: No auth context', 'UNAUTHORIZED', 401);
83
+ return;
84
+ }
85
+ const organizationId = req.params['organizationId'] ??
86
+ req.params['orgId'] ??
87
+ req.validated?.params?.['organizationId'] ??
88
+ req.validated?.body?.['organizationId'];
89
+ if (!organizationId || typeof organizationId !== 'string') {
90
+ (0, utils_1.failure)(res, req, 'Bad Request: Missing organization context', 'BAD_REQUEST', 400);
91
+ return;
92
+ }
93
+ try {
94
+ const perms = await permissionCache.resolveOrganizationPermissions(ctx.accountId, organizationId, async () => {
95
+ return resolveFn(ctx.accountId, organizationId);
96
+ });
97
+ if (!permission_checker_1.PermissionChecker.hasAll(perms, permissions)) {
98
+ const missing = permissions.filter((p) => !permission_checker_1.PermissionChecker.has(perms, p));
99
+ (0, utils_1.failure)(res, req, `Forbidden: Missing required organization permissions: [${missing.join(', ')}]`, 'FORBIDDEN', 403);
100
+ return;
101
+ }
102
+ next();
103
+ }
104
+ catch (err) {
105
+ next(err);
106
+ }
107
+ };
108
+ };
109
+ return {
110
+ requireOrgPermission,
111
+ requireAnyOrgPermission,
112
+ requireAllOrgPermissions,
113
+ };
114
+ };
115
+ exports.createRequireOrgPermission = createRequireOrgPermission;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @file require-platform-permission.middleware.ts
3
+ * @description Express middleware generator to enforce platform-scoped permission checks.
4
+ */
5
+ import { RequestHandler } from 'express';
6
+ import { PlatformPermission } from '../enums';
7
+ import { PermissionCacheService } from '../authorization/permission-cache.service';
8
+ /**
9
+ * Creates requirePlatformPermission middleware.
10
+ *
11
+ * @param {PermissionCacheService} permissionCache - Shared Redis cache service.
12
+ * @param {(accountId: string) => Promise<string[]>} resolveFn - Cache miss database/API lookup function.
13
+ * @returns {object} Platform permission middlewares.
14
+ */
15
+ export declare const createRequirePlatformPermission: (permissionCache: PermissionCacheService, resolveFn: (accountId: string) => Promise<string[]>) => {
16
+ requirePlatformPermission: (permission: PlatformPermission) => RequestHandler;
17
+ requireAnyPlatformPermission: (...permissions: PlatformPermission[]) => RequestHandler;
18
+ requireAllPlatformPermissions: (...permissions: PlatformPermission[]) => RequestHandler;
19
+ };