@discover-cloud/shared 1.3.0 → 1.5.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 (81) hide show
  1. package/dist/authorization/index.d.ts +2 -2
  2. package/dist/authorization/permission-cache.service.js +27 -11
  3. package/dist/context/index.d.ts +1 -0
  4. package/dist/context/index.js +17 -0
  5. package/dist/context/request-context.storage.d.ts +21 -0
  6. package/dist/context/request-context.storage.js +37 -0
  7. package/dist/contracts/auth-service/account.dto.d.ts +1 -1
  8. package/dist/contracts/index.d.ts +1 -0
  9. package/dist/contracts/index.js +17 -0
  10. package/dist/contracts/visibility.d.ts +63 -0
  11. package/dist/contracts/visibility.js +10 -0
  12. package/dist/dtos/audit-service.types.d.ts +42 -0
  13. package/dist/dtos/audit-service.types.js +31 -0
  14. package/dist/dtos/auth-service.dto.d.ts +16 -1
  15. package/dist/dtos/cloud-service.dto.d.ts +9 -3
  16. package/dist/dtos/index.d.ts +6 -5
  17. package/dist/dtos/index.js +1 -0
  18. package/dist/dtos/insights-service.dto.d.ts +174 -13
  19. package/dist/dtos/response.dto.d.ts +6 -0
  20. package/dist/dtos/user-service.dto.d.ts +52 -7
  21. package/dist/enums/domain.enums.d.ts +52 -26
  22. package/dist/enums/domain.enums.js +64 -40
  23. package/dist/enums/index.d.ts +2 -2
  24. package/dist/errors/app-error.js +1 -1
  25. package/dist/errors/http-errors.d.ts +1 -1
  26. package/dist/errors/http-errors.js +26 -26
  27. package/dist/errors/index.d.ts +2 -2
  28. package/dist/http/index.d.ts +2 -2
  29. package/dist/http/request-context.d.ts +2 -2
  30. package/dist/http/request-context.js +5 -5
  31. package/dist/http/service-client.d.ts +2 -2
  32. package/dist/http/service-client.js +2 -2
  33. package/dist/index.d.ts +11 -10
  34. package/dist/index.js +1 -0
  35. package/dist/jwt/index.d.ts +3 -3
  36. package/dist/jwt/machine-jwt-verifier.d.ts +1 -1
  37. package/dist/jwt/machine-jwt-verifier.js +13 -14
  38. package/dist/jwt/machine-token-client.d.ts +1 -1
  39. package/dist/jwt/machine-token-client.js +8 -8
  40. package/dist/jwt/user-jwt-verifier.d.ts +1 -1
  41. package/dist/jwt/user-jwt-verifier.js +17 -16
  42. package/dist/messaging/audit.publisher.d.ts +14 -0
  43. package/dist/messaging/audit.publisher.js +49 -0
  44. package/dist/messaging/index.d.ts +1 -0
  45. package/dist/messaging/index.js +1 -0
  46. package/dist/messaging/rabbitmq.client.d.ts +17 -5
  47. package/dist/messaging/rabbitmq.client.js +83 -36
  48. package/dist/middleware/error-handler.middleware.d.ts +2 -2
  49. package/dist/middleware/error-handler.middleware.js +10 -10
  50. package/dist/middleware/index.d.ts +10 -10
  51. package/dist/middleware/request-context.middleware.d.ts +1 -12
  52. package/dist/middleware/request-context.middleware.js +17 -42
  53. package/dist/middleware/require-auth.middleware.d.ts +3 -19
  54. package/dist/middleware/require-auth.middleware.js +17 -26
  55. package/dist/middleware/require-machine.middleware.d.ts +2 -27
  56. package/dist/middleware/require-machine.middleware.js +30 -25
  57. package/dist/middleware/require-org-permission-from-body.middleware.js +1 -2
  58. package/dist/middleware/require-org-permission.middleware.d.ts +3 -3
  59. package/dist/middleware/require-org-permission.middleware.js +28 -28
  60. package/dist/middleware/require-platform-permission.middleware.d.ts +3 -3
  61. package/dist/middleware/require-platform-permission.middleware.js +10 -10
  62. package/dist/middleware/require-user.middleware.d.ts +1 -1
  63. package/dist/middleware/require-user.middleware.js +2 -2
  64. package/dist/middleware/require-workspace-permission.middleware.d.ts +3 -3
  65. package/dist/middleware/require-workspace-permission.middleware.js +25 -25
  66. package/dist/middleware/validate.middleware.d.ts +3 -3
  67. package/dist/middleware/validate.middleware.js +1 -1
  68. package/dist/types/audit.types.d.ts +39 -0
  69. package/dist/types/audit.types.js +30 -0
  70. package/dist/types/express.types.d.ts +14 -7
  71. package/dist/types/express.types.js +10 -10
  72. package/dist/types/index.d.ts +1 -1
  73. package/dist/utils/env.util.js +1 -1
  74. package/dist/utils/index.d.ts +5 -5
  75. package/dist/utils/logger.util.d.ts +5 -0
  76. package/dist/utils/logger.util.js +46 -8
  77. package/dist/utils/request-context.als.d.ts +10 -0
  78. package/dist/utils/request-context.als.js +5 -0
  79. package/dist/utils/response.util.d.ts +1 -1
  80. package/dist/utils/url-safety.util.js +4 -1
  81. package/package.json +1 -1
@@ -35,37 +35,66 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.RabbitMQClient = void 0;
37
37
  const amqplib = __importStar(require("amqplib"));
38
+ const logger_util_1 = require("../utils/logger.util");
38
39
  class RabbitMQClient {
39
- constructor(url) {
40
+ constructor(url, logger = logger_util_1.consoleLogger) {
40
41
  this.connection = null;
41
42
  this.channel = null;
42
43
  this.exchangeName = 'discovercloud.topic';
44
+ this.reconnecting = false;
43
45
  this.url = url;
46
+ this.logger = logger;
44
47
  }
48
+ /**
49
+ * Establishes a connection to RabbitMQ.
50
+ * Blocks until a connection is successfully established.
51
+ */
45
52
  async connect() {
46
53
  if (this.connection && this.channel) {
47
54
  return;
48
55
  }
49
- try {
50
- this.connection = await amqplib.connect(this.url);
51
- this.connection.on('error', (err) => {
52
- console.error('RabbitMQ Connection Error:', err);
53
- });
54
- this.connection.on('close', () => {
55
- console.warn('RabbitMQ Connection Closed. Reconnecting...');
56
- this.connection = null;
57
- this.channel = null;
58
- setTimeout(() => this.connect(), 5000);
59
- });
60
- this.channel = await this.connection.createChannel();
61
- // Ensure the main topic exchange exists
62
- await this.channel.assertExchange(this.exchangeName, 'topic', { durable: true });
63
- console.log('✅ Connected to RabbitMQ');
56
+ while (!this.channel) {
57
+ try {
58
+ this.logger.info('Connecting to RabbitMQ...');
59
+ this.connection = await amqplib.connect(this.url);
60
+ this.connection.on('error', (err) => {
61
+ this.logger.error(err, 'RabbitMQ Connection Error');
62
+ });
63
+ this.connection.on('close', () => {
64
+ this.logger.warn('RabbitMQ Connection Closed');
65
+ this.connection = null;
66
+ this.channel = null;
67
+ if (!this.reconnecting) {
68
+ void this.reconnect();
69
+ }
70
+ });
71
+ this.channel = await this.connection.createChannel();
72
+ await this.channel.assertExchange(this.exchangeName, 'topic', {
73
+ durable: true,
74
+ });
75
+ this.logger.info('✅ RabbitMQ connected');
76
+ return;
77
+ }
78
+ catch (err) {
79
+ this.logger.error(err, 'RabbitMQ unavailable. Retrying in 5 seconds...');
80
+ await this.delay(5000);
81
+ }
64
82
  }
65
- catch (error) {
66
- console.error('❌ Failed to connect to RabbitMQ:', error);
67
- setTimeout(() => this.connect(), 5000);
83
+ }
84
+ /**
85
+ * Background reconnect loop.
86
+ */
87
+ async reconnect() {
88
+ this.reconnecting = true;
89
+ while (!this.channel) {
90
+ try {
91
+ await this.connect();
92
+ }
93
+ catch {
94
+ await this.delay(5000);
95
+ }
68
96
  }
97
+ this.reconnecting = false;
69
98
  }
70
99
  async publishEvent(routingKey, payload) {
71
100
  if (!this.channel) {
@@ -85,43 +114,61 @@ class RabbitMQClient {
85
114
  if (options.prefetch) {
86
115
  await this.channel.prefetch(options.prefetch);
87
116
  }
88
- // Dead Letter configuration
89
117
  const dlxName = `${this.exchangeName}.dlx`;
90
118
  const dlqName = `${queueName}.dlq`;
91
- await this.channel.assertExchange(dlxName, 'topic', { durable: true });
92
- await this.channel.assertQueue(dlqName, { durable: true });
119
+ await this.channel.assertExchange(dlxName, 'topic', {
120
+ durable: true,
121
+ });
122
+ await this.channel.assertQueue(dlqName, {
123
+ durable: true,
124
+ });
93
125
  await this.channel.bindQueue(dlqName, dlxName, routingKey);
94
- // Assert main queue with DLX bindings
95
126
  await this.channel.assertQueue(queueName, {
96
127
  durable: true,
97
128
  deadLetterExchange: dlxName,
98
129
  deadLetterRoutingKey: routingKey,
99
130
  });
100
131
  await this.channel.bindQueue(queueName, this.exchangeName, routingKey);
101
- this.channel.consume(queueName, async (msg) => {
102
- if (!msg)
132
+ await this.channel.consume(queueName, async (msg) => {
133
+ if (!msg) {
103
134
  return;
135
+ }
104
136
  try {
105
137
  const payload = JSON.parse(msg.content.toString());
106
138
  await handler(payload);
107
- this.channel.ack(msg);
139
+ this.channel?.ack(msg);
108
140
  }
109
- catch (error) {
110
- console.error(`Error processing message from ${queueName}:`, error);
111
- // Nack the message and don't requeue, so it goes to the DLX
112
- this.channel.nack(msg, false, false);
141
+ catch (err) {
142
+ this.logger.error(err, `Error processing ${queueName}`);
143
+ this.channel?.nack(msg, false, false);
113
144
  }
114
145
  });
115
- console.log(`📡 Subscribed to queue [${queueName}] with routing key [${routingKey}]`);
146
+ this.logger.info(`📡 Subscribed to ${queueName} (${routingKey})`);
116
147
  }
117
148
  async close() {
118
- if (this.channel) {
119
- await this.channel.close();
149
+ try {
150
+ this.reconnecting = true; // Prevents triggering reconnect during manual close
151
+ if (this.connection) {
152
+ this.connection.removeAllListeners('close');
153
+ }
154
+ if (this.channel) {
155
+ await this.channel.close();
156
+ }
157
+ if (this.connection) {
158
+ await this.connection.close();
159
+ }
120
160
  }
121
- if (this.connection) {
122
- await this.connection.close();
161
+ finally {
162
+ this.channel = null;
163
+ this.connection = null;
164
+ this.reconnecting = false;
123
165
  }
124
- console.log('RabbitMQ connection closed.');
166
+ this.logger.info('RabbitMQ disconnected');
167
+ }
168
+ delay(ms) {
169
+ return new Promise((resolve) => {
170
+ setTimeout(resolve, ms);
171
+ });
125
172
  }
126
173
  }
127
174
  exports.RabbitMQClient = RabbitMQClient;
@@ -2,8 +2,8 @@
2
2
  * @file error-handler.middleware.ts
3
3
  * @description Central Express error handler. Register as the final middleware.
4
4
  */
5
- import { ErrorRequestHandler } from "express";
6
- import { ILogger } from "../utils";
5
+ import { ErrorRequestHandler } from 'express';
6
+ import { ILogger } from '../utils';
7
7
  /**
8
8
  * Creates global Express error middleware.
9
9
  */
@@ -13,9 +13,9 @@ const utils_1 = require("../utils");
13
13
  */
14
14
  function isMalformedJsonError(err) {
15
15
  return (err instanceof SyntaxError &&
16
- "status" in err &&
17
- typeof err.status === "number" &&
18
- "body" in err);
16
+ 'status' in err &&
17
+ typeof err.status === 'number' &&
18
+ 'body' in err);
19
19
  }
20
20
  /**
21
21
  * Converts any thrown value into Error.
@@ -24,7 +24,7 @@ function normalizeError(err) {
24
24
  if (err instanceof Error) {
25
25
  return err;
26
26
  }
27
- if (typeof err === "string") {
27
+ if (typeof err === 'string') {
28
28
  return new Error(err);
29
29
  }
30
30
  return new Error(JSON.stringify(err));
@@ -40,16 +40,16 @@ function createGlobalErrorHandler(logger = utils_1.noopLogger) {
40
40
  * Malformed JSON
41
41
  */
42
42
  if (isMalformedJsonError(err) && err.status === 400) {
43
- logger.warn({ requestId, err: error }, "Malformed JSON payload");
44
- (0, utils_1.failure)(res, req, "Invalid JSON payload", "BAD_REQUEST", 400);
43
+ logger.warn({ requestId, err: error }, 'Malformed JSON payload');
44
+ (0, utils_1.failure)(res, req, 'Invalid JSON payload', 'BAD_REQUEST', 400);
45
45
  return;
46
46
  }
47
47
  /**
48
48
  * Validation failures
49
49
  */
50
50
  if (err instanceof zod_1.ZodError) {
51
- logger.warn({ requestId, err: error }, "Request validation failed");
52
- (0, utils_1.failure)(res, req, "Validation failed", "VALIDATION_ERROR", 400, err.flatten());
51
+ logger.warn({ requestId, err: error }, 'Request validation failed');
52
+ (0, utils_1.failure)(res, req, 'Validation failed', 'VALIDATION_ERROR', 400, err.flatten());
53
53
  return;
54
54
  }
55
55
  /**
@@ -73,7 +73,7 @@ function createGlobalErrorHandler(logger = utils_1.noopLogger) {
73
73
  /**
74
74
  * Unknown crashes
75
75
  */
76
- logger.error({ requestId, err: error }, "Unhandled exception");
77
- (0, utils_1.failure)(res, req, "Internal Server Error", "INTERNAL_SERVER_ERROR", 500);
76
+ logger.error({ requestId, err: error }, 'Unhandled exception');
77
+ (0, utils_1.failure)(res, req, 'Internal Server Error', 'INTERNAL_SERVER_ERROR', 500);
78
78
  };
79
79
  }
@@ -1,10 +1,10 @@
1
- export * from "./error-handler.middleware";
2
- export * from "./validate.middleware";
3
- export * from "./request-context.middleware";
4
- export * from "./require-machine.middleware";
5
- export * from "./require-auth.middleware";
6
- export * from "./require-user.middleware";
7
- export * from "./require-org-permission.middleware";
8
- export * from "./require-workspace-permission.middleware";
9
- export * from "./require-platform-permission.middleware";
10
- export * from "./require-org-permission-from-body.middleware";
1
+ export * from './error-handler.middleware';
2
+ export * from './validate.middleware';
3
+ export * from './request-context.middleware';
4
+ export * from './require-machine.middleware';
5
+ export * from './require-auth.middleware';
6
+ export * from './require-user.middleware';
7
+ export * from './require-org-permission.middleware';
8
+ export * from './require-workspace-permission.middleware';
9
+ export * from './require-platform-permission.middleware';
10
+ export * from './require-org-permission-from-body.middleware';
@@ -2,16 +2,5 @@
2
2
  * @file request-context.middleware.ts
3
3
  * @description Express middleware to assign request tracing and audit metadata context.
4
4
  */
5
- import { Request, Response, NextFunction } from "express";
6
- /**
7
- * Attaches request metadata used for:
8
- * - distributed tracing
9
- * - audit logs
10
- * - debugging correlation
11
- *
12
- * @param {Request} req - Express request object.
13
- * @param {Response} res - Express response object.
14
- * @param {NextFunction} next - Express next middleware function.
15
- * @returns {void}
16
- */
5
+ import { Request, Response, NextFunction } from 'express';
17
6
  export declare const requestContext: (req: Request, res: Response, next: NextFunction) => void;
@@ -6,68 +6,43 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.requestContext = void 0;
8
8
  const crypto_1 = require("crypto");
9
- /**
10
- * Normalizes IPv4-mapped IPv6 addresses.
11
- *
12
- * @param {string | null} ip - Raw IP.
13
- * @returns {string | null} Normalized IP.
14
- */
9
+ const request_context_storage_1 = require("../context/request-context.storage");
15
10
  function normalizeIp(ip) {
16
- if (!ip) {
11
+ if (!ip)
17
12
  return null;
13
+ if (ip === '::1') {
14
+ return '127.0.0.1';
18
15
  }
19
- if (ip === "::1") {
20
- return "127.0.0.1";
21
- }
22
- if (ip.startsWith("::ffff:")) {
16
+ if (ip.startsWith('::ffff:')) {
23
17
  return ip.substring(7);
24
18
  }
25
19
  return ip;
26
20
  }
27
- /**
28
- * Resolves original client IP forwarded by the API Gateway.
29
- *
30
- * Priority:
31
- * 1. x-forwarded-for
32
- * 2. x-real-ip
33
- * 3. socket fallback (local development only)
34
- *
35
- * @param {Request} req - Express request object.
36
- * @returns {string | null} Client IP address.
37
- */
38
21
  function resolveClientIp(req) {
39
- const forwarded = req.headers["x-forwarded-for"];
22
+ const forwarded = req.headers['x-forwarded-for'];
40
23
  if (forwarded) {
41
24
  const value = Array.isArray(forwarded) ? forwarded[0] : forwarded;
42
- return normalizeIp(value.split(",")[0].trim());
25
+ return normalizeIp(value.split(',')[0].trim());
43
26
  }
44
- const realIp = req.headers["x-real-ip"];
27
+ const realIp = req.headers['x-real-ip'];
45
28
  if (realIp) {
46
29
  return normalizeIp(Array.isArray(realIp) ? realIp[0] : realIp);
47
30
  }
48
31
  return normalizeIp(req.socket.remoteAddress ?? null);
49
32
  }
50
- /**
51
- * Attaches request metadata used for:
52
- * - distributed tracing
53
- * - audit logs
54
- * - debugging correlation
55
- *
56
- * @param {Request} req - Express request object.
57
- * @param {Response} res - Express response object.
58
- * @param {NextFunction} next - Express next middleware function.
59
- * @returns {void}
60
- */
61
33
  const requestContext = (req, res, next) => {
62
- const upstream = req.headers["x-request-id"];
34
+ const upstream = req.headers['x-request-id'];
63
35
  const requestId = Array.isArray(upstream) ? upstream[0] : (upstream ?? (0, crypto_1.randomUUID)());
64
- req.id = requestId;
65
- req.requestContext = {
36
+ // Explicitly type this as AuditRequestContext so TS knows it's mutable
37
+ // and allows the `.actor` property to be assigned by the auth middlewares later.
38
+ const context = {
66
39
  requestId,
67
40
  ipAddress: resolveClientIp(req),
68
- userAgent: req.headers["user-agent"] ?? null,
41
+ userAgent: req.headers['user-agent'] ?? null,
69
42
  };
70
- res.setHeader("x-request-id", requestId);
71
- next();
43
+ req.id = requestId;
44
+ req.requestContext = context;
45
+ res.setHeader('x-request-id', requestId);
46
+ request_context_storage_1.requestContextStorage.run(context, () => next());
72
47
  };
73
48
  exports.requestContext = requestContext;
@@ -3,29 +3,13 @@
3
3
  * @description Authentication boundary middleware. Verifies the internal gateway JWT,
4
4
  * builds the auth context, and gates endpoints.
5
5
  */
6
- import { Request, Response, NextFunction } from "express";
7
- import { ILogger } from "../utils";
8
- import { UserJwtVerifier } from "../jwt/user-jwt-verifier";
9
- /**
10
- * Middleware handling authentication validation.
11
- * Extracts the JWT, verifies it via UserJwtVerifier, and attaches the AuthContext.
12
- */
6
+ import { Request, Response, NextFunction } from 'express';
7
+ import { ILogger } from '../utils';
8
+ import { UserJwtVerifier } from '../jwt/user-jwt-verifier';
13
9
  export declare class RequireAuthMiddleware {
14
10
  private readonly verifier;
15
11
  private readonly logger;
16
- /**
17
- * @param {UserJwtVerifier} verifier - The token verification engine.
18
- * @param {ILogger} [logger] - Optional logger instance.
19
- */
20
12
  constructor(verifier: UserJwtVerifier, logger?: ILogger);
21
- /**
22
- * Core Express middleware handler for authenticating requests.
23
- *
24
- * @param {Request} req - Express request object.
25
- * @param {Response} res - Express response object.
26
- * @param {NextFunction} next - Express next function.
27
- * @returns {Promise<void>}
28
- */
29
13
  handle: (req: Request, res: Response, next: NextFunction) => Promise<void>;
30
14
  private extractBearer;
31
15
  private handleJwtError;
@@ -8,41 +8,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.RequireAuthMiddleware = void 0;
9
9
  const jose_1 = require("jose");
10
10
  const utils_1 = require("../utils");
11
- /**
12
- * Middleware handling authentication validation.
13
- * Extracts the JWT, verifies it via UserJwtVerifier, and attaches the AuthContext.
14
- */
11
+ const request_context_storage_1 = require("../context/request-context.storage");
15
12
  class RequireAuthMiddleware {
16
- /**
17
- * @param {UserJwtVerifier} verifier - The token verification engine.
18
- * @param {ILogger} [logger] - Optional logger instance.
19
- */
20
13
  constructor(verifier, logger = utils_1.noopLogger) {
21
14
  this.verifier = verifier;
22
15
  this.logger = logger;
23
- /**
24
- * Core Express middleware handler for authenticating requests.
25
- *
26
- * @param {Request} req - Express request object.
27
- * @param {Response} res - Express response object.
28
- * @param {NextFunction} next - Express next function.
29
- * @returns {Promise<void>}
30
- */
31
16
  this.handle = async (req, res, next) => {
32
17
  const token = this.extractBearer(req);
33
18
  if (!token) {
34
- (0, utils_1.failure)(res, req, "Missing Authorization header", "UNAUTHORIZED", 401);
19
+ (0, utils_1.failure)(res, req, 'Missing Authorization header', 'UNAUTHORIZED', 401);
35
20
  return;
36
21
  }
37
22
  try {
38
23
  const { payload, context } = await this.verifier.buildAccessContext(token);
39
24
  req.authPayload = payload;
40
25
  req.authContext = context;
26
+ // Attach audit context. Will throw if requestContextStorage is not initialized.
27
+ request_context_storage_1.RequestContextProvider.setActor(context);
41
28
  this.logger.debug({
42
29
  requestId: req.id,
43
30
  identity: context.accountId,
44
31
  type: context.type,
45
- }, "Request authenticated");
32
+ }, 'Request authenticated');
46
33
  next();
47
34
  }
48
35
  catch (err) {
@@ -55,29 +42,33 @@ class RequireAuthMiddleware {
55
42
  --------------------------------------------------------------------------- */
56
43
  extractBearer(req) {
57
44
  const header = req.headers.authorization;
58
- if (!header) {
45
+ if (!header)
59
46
  return null;
60
- }
61
47
  const [scheme, token] = header.trim().split(/\s+/);
62
- if (scheme !== "Bearer" || !token) {
48
+ if (scheme !== 'Bearer' || !token)
63
49
  return null;
64
- }
65
50
  return token;
66
51
  }
67
52
  handleJwtError(err, req, res) {
68
53
  if (err instanceof jose_1.errors.JWTExpired) {
69
- (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);
70
55
  return;
71
56
  }
72
57
  if (err instanceof jose_1.errors.JWTClaimValidationFailed ||
73
58
  err instanceof jose_1.errors.JWSSignatureVerificationFailed ||
74
59
  err instanceof jose_1.errors.JWSInvalid ||
75
60
  err instanceof jose_1.errors.JWTInvalid) {
76
- (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);
77
68
  return;
78
69
  }
79
- this.logger.error({ err, requestId: req.id }, "RequireAuthMiddleware: unexpected error during JWT verification");
80
- (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);
81
72
  }
82
73
  }
83
74
  exports.RequireAuthMiddleware = RequireAuthMiddleware;
@@ -3,36 +3,11 @@
3
3
  * @description Express middleware enforcing auth-service issued machine JWT verification
4
4
  * for service-to-service communication endpoints.
5
5
  */
6
- import { RequestHandler } from "express";
7
- import { ILogger } from "../utils/logger.util";
8
- /**
9
- * Configuration options for machine authentication middleware.
10
- */
6
+ import { RequestHandler } from 'express';
7
+ import { ILogger } from '../utils/logger.util';
11
8
  export interface RequireMachineOptions {
12
- /**
13
- * Auth-service JWKS endpoint used to verify machine JWT signatures.
14
- */
15
9
  authServiceJwksUri?: string;
16
- /**
17
- * Service identifiers allowed to access this endpoint.
18
- */
19
10
  allowedServices?: string[];
20
- /**
21
- * Optional structured logger.
22
- */
23
11
  logger?: ILogger;
24
12
  }
25
- /**
26
- * Creates middleware requiring a valid machine JWT.
27
- *
28
- * Flow:
29
- * - Extract Bearer token
30
- * - Verify RS256 signature using auth-service JWKS
31
- * - Validate machine token claims
32
- * - Apply service allowlist
33
- * - Attach MachineContext
34
- *
35
- * @param {RequireMachineOptions} [options] - Middleware configuration options.
36
- * @returns {RequestHandler} Express middleware handler.
37
- */
38
13
  export declare function requireMachine(options?: RequireMachineOptions): RequestHandler;
@@ -42,29 +42,20 @@ exports.requireMachine = requireMachine;
42
42
  const jose = __importStar(require("jose"));
43
43
  const logger_util_1 = require("../utils/logger.util");
44
44
  const machine_jwt_verifier_1 = require("../jwt/machine-jwt-verifier");
45
- /**
46
- * Creates middleware requiring a valid machine JWT.
47
- *
48
- * Flow:
49
- * - Extract Bearer token
50
- * - Verify RS256 signature using auth-service JWKS
51
- * - Validate machine token claims
52
- * - Apply service allowlist
53
- * - Attach MachineContext
54
- *
55
- * @param {RequireMachineOptions} [options] - Middleware configuration options.
56
- * @returns {RequestHandler} Express middleware handler.
57
- */
45
+ const request_context_storage_1 = require("../context/request-context.storage");
58
46
  function requireMachine(options = {}) {
59
47
  const logger = options.logger ?? logger_util_1.noopLogger;
60
48
  const verifier = new machine_jwt_verifier_1.MachineJwtVerifier(options.authServiceJwksUri);
61
49
  const { allowedServices } = options;
62
50
  return async (req, res, next) => {
63
51
  const header = req.headers.authorization;
64
- if (!header?.startsWith("Bearer ")) {
52
+ if (!header?.startsWith('Bearer ')) {
65
53
  res.status(401).json({
66
54
  success: false,
67
- error: { code: "UNAUTHORIZED", message: "Missing Authorization header" },
55
+ error: {
56
+ code: 'UNAUTHORIZED',
57
+ message: 'Missing Authorization header',
58
+ },
68
59
  });
69
60
  return;
70
61
  }
@@ -72,31 +63,33 @@ function requireMachine(options = {}) {
72
63
  try {
73
64
  const payload = await verifier.verify(token);
74
65
  if (allowedServices && !allowedServices.includes(payload.serviceId)) {
75
- logger.warn({ requestId: req.id, serviceId: payload.serviceId }, "[requireMachine] Service rejected");
66
+ logger.warn({ requestId: req.id, serviceId: payload.serviceId }, '[requireMachine] Service rejected');
76
67
  res.status(403).json({
77
68
  success: false,
78
69
  error: {
79
- code: "FORBIDDEN",
80
- message: "Service is not permitted to access this endpoint",
70
+ code: 'FORBIDDEN',
71
+ message: 'Service is not permitted to access this endpoint',
81
72
  },
82
73
  });
83
74
  return;
84
75
  }
85
76
  const context = {
86
- type: "machine",
77
+ type: 'machine',
87
78
  serviceId: payload.serviceId,
88
79
  scopes: payload.scopes,
89
80
  };
90
81
  req.authPayload = payload;
91
82
  req.authContext = context;
92
- logger.debug({ requestId: req.id, serviceId: payload.serviceId, jti: payload.jti }, "[requireMachine] Machine authenticated");
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');
93
86
  next();
94
87
  }
95
88
  catch (err) {
96
89
  if (err instanceof jose.errors.JWTExpired) {
97
90
  res.status(401).json({
98
91
  success: false,
99
- error: { code: "TOKEN_EXPIRED", message: "Machine token expired" },
92
+ error: { code: 'TOKEN_EXPIRED', message: 'Machine token expired' },
100
93
  });
101
94
  return;
102
95
  }
@@ -106,16 +99,28 @@ function requireMachine(options = {}) {
106
99
  err instanceof jose.errors.JWTInvalid) {
107
100
  res.status(401).json({
108
101
  success: false,
109
- error: { code: "INVALID_TOKEN", message: "Invalid machine token" },
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
+ },
110
115
  });
111
116
  return;
112
117
  }
113
- logger.error({ err, requestId: req.id }, "[requireMachine] Machine authentication failure");
118
+ logger.error({ err, requestId: req.id }, '[requireMachine] Machine authentication failure');
114
119
  res.status(503).json({
115
120
  success: false,
116
121
  error: {
117
- code: "SERVICE_UNAVAILABLE",
118
- message: "Machine authentication temporarily unavailable",
122
+ code: 'SERVICE_UNAVAILABLE',
123
+ message: 'Machine authentication temporarily unavailable',
119
124
  },
120
125
  });
121
126
  }
@@ -9,8 +9,7 @@ exports.createRequireOrgPermissionFromBody = void 0;
9
9
  const createRequireOrgPermissionFromBody = (requireOrgPermission) => {
10
10
  const requireOrgPermissionFromBody = (permission) => {
11
11
  return async (req, res, next) => {
12
- const organizationId = req.body?.organizationId ??
13
- req.validated?.body?.organizationId;
12
+ const organizationId = req.body?.organizationId ?? req.validated?.body?.organizationId;
14
13
  if (!organizationId) {
15
14
  return next();
16
15
  }
@@ -2,9 +2,9 @@
2
2
  * @file require-org-permission.middleware.ts
3
3
  * @description Express middleware generator to enforce organization-scoped permission checks.
4
4
  */
5
- import { RequestHandler } from "express";
6
- import { Permission } from "../enums";
7
- import { PermissionCacheService } from "../authorization/permission-cache.service";
5
+ import { RequestHandler } from 'express';
6
+ import { Permission } from '../enums';
7
+ import { PermissionCacheService } from '../authorization/permission-cache.service';
8
8
  /**
9
9
  * Creates requireOrgPermission middleware factory.
10
10
  *