@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
@@ -1,2 +1,2 @@
1
- export * from "./permissions.enums";
2
- export * from "./domain.enums";
1
+ export * from './permissions.enums';
2
+ export * from './domain.enums';
@@ -23,7 +23,7 @@ class AppError extends Error {
23
23
  this.statusCode = statusCode;
24
24
  this.details = details;
25
25
  this.name = this.constructor.name;
26
- if (typeof Error.captureStackTrace === "function") {
26
+ if (typeof Error.captureStackTrace === 'function') {
27
27
  Error.captureStackTrace(this, this.constructor);
28
28
  }
29
29
  Object.freeze(this);
@@ -2,7 +2,7 @@
2
2
  * @file http-errors.ts
3
3
  * @description Operational error subclasses mapping common HTTP status codes to AppError types.
4
4
  */
5
- import { AppError } from "./app-error";
5
+ import { AppError } from './app-error';
6
6
  /**
7
7
  * 400 Bad Request error.
8
8
  */
@@ -14,8 +14,8 @@ class BadRequestError extends app_error_1.AppError {
14
14
  * @param {string} [message] - Error explanation message.
15
15
  * @param {unknown} [details] - Detailed error context.
16
16
  */
17
- constructor(message = "Bad Request", details) {
18
- super("BAD_REQUEST", 400, message, details);
17
+ constructor(message = 'Bad Request', details) {
18
+ super('BAD_REQUEST', 400, message, details);
19
19
  }
20
20
  }
21
21
  exports.BadRequestError = BadRequestError;
@@ -27,8 +27,8 @@ class UnauthorizedError extends app_error_1.AppError {
27
27
  * @param {string} [message] - Error explanation message.
28
28
  * @param {unknown} [details] - Detailed error context.
29
29
  */
30
- constructor(message = "Unauthorized", details) {
31
- super("UNAUTHORIZED", 401, message, details);
30
+ constructor(message = 'Unauthorized', details) {
31
+ super('UNAUTHORIZED', 401, message, details);
32
32
  }
33
33
  }
34
34
  exports.UnauthorizedError = UnauthorizedError;
@@ -40,8 +40,8 @@ class ForbiddenError extends app_error_1.AppError {
40
40
  * @param {string} [message] - Error explanation message.
41
41
  * @param {unknown} [details] - Detailed error context.
42
42
  */
43
- constructor(message = "Forbidden", details) {
44
- super("FORBIDDEN", 403, message, details);
43
+ constructor(message = 'Forbidden', details) {
44
+ super('FORBIDDEN', 403, message, details);
45
45
  }
46
46
  }
47
47
  exports.ForbiddenError = ForbiddenError;
@@ -53,8 +53,8 @@ class NotFoundError extends app_error_1.AppError {
53
53
  * @param {string} [message] - Error explanation message.
54
54
  * @param {unknown} [details] - Detailed error context.
55
55
  */
56
- constructor(message = "Not Found", details) {
57
- super("NOT_FOUND", 404, message, details);
56
+ constructor(message = 'Not Found', details) {
57
+ super('NOT_FOUND', 404, message, details);
58
58
  }
59
59
  }
60
60
  exports.NotFoundError = NotFoundError;
@@ -66,8 +66,8 @@ class GoneError extends app_error_1.AppError {
66
66
  * @param {string} [message] - Error explanation message.
67
67
  * @param {unknown} [details] - Detailed error context.
68
68
  */
69
- constructor(message = "Gone", details) {
70
- super("GONE", 410, message, details);
69
+ constructor(message = 'Gone', details) {
70
+ super('GONE', 410, message, details);
71
71
  }
72
72
  }
73
73
  exports.GoneError = GoneError;
@@ -79,8 +79,8 @@ class ConflictError extends app_error_1.AppError {
79
79
  * @param {string} [message] - Error explanation message.
80
80
  * @param {unknown} [details] - Detailed error context.
81
81
  */
82
- constructor(message = "Conflict", details) {
83
- super("CONFLICT", 409, message, details);
82
+ constructor(message = 'Conflict', details) {
83
+ super('CONFLICT', 409, message, details);
84
84
  }
85
85
  }
86
86
  exports.ConflictError = ConflictError;
@@ -92,8 +92,8 @@ class UnprocessableEntityError extends app_error_1.AppError {
92
92
  * @param {string} [message] - Error explanation message.
93
93
  * @param {unknown} [details] - Detailed error context.
94
94
  */
95
- constructor(message = "Unprocessable Entity", details) {
96
- super("UNPROCESSABLE_ENTITY", 422, message, details);
95
+ constructor(message = 'Unprocessable Entity', details) {
96
+ super('UNPROCESSABLE_ENTITY', 422, message, details);
97
97
  }
98
98
  }
99
99
  exports.UnprocessableEntityError = UnprocessableEntityError;
@@ -105,8 +105,8 @@ class TooManyRequestsError extends app_error_1.AppError {
105
105
  * @param {string} [message] - Error explanation message.
106
106
  * @param {unknown} [details] - Detailed error context.
107
107
  */
108
- constructor(message = "Too Many Requests", details) {
109
- super("TOO_MANY_REQUESTS", 429, message, details);
108
+ constructor(message = 'Too Many Requests', details) {
109
+ super('TOO_MANY_REQUESTS', 429, message, details);
110
110
  }
111
111
  }
112
112
  exports.TooManyRequestsError = TooManyRequestsError;
@@ -118,8 +118,8 @@ class InternalServerError extends app_error_1.AppError {
118
118
  * @param {string} [message] - Error explanation message.
119
119
  * @param {unknown} [details] - Detailed error context.
120
120
  */
121
- constructor(message = "Internal Server Error", details) {
122
- super("INTERNAL_SERVER_ERROR", 500, message, details);
121
+ constructor(message = 'Internal Server Error', details) {
122
+ super('INTERNAL_SERVER_ERROR', 500, message, details);
123
123
  }
124
124
  }
125
125
  exports.InternalServerError = InternalServerError;
@@ -131,8 +131,8 @@ class ServiceUnavailableError extends app_error_1.AppError {
131
131
  * @param {string} [message] - Error explanation message.
132
132
  * @param {unknown} [details] - Detailed error context.
133
133
  */
134
- constructor(message = "Service Unavailable", details) {
135
- super("SERVICE_UNAVAILABLE", 503, message, details);
134
+ constructor(message = 'Service Unavailable', details) {
135
+ super('SERVICE_UNAVAILABLE', 503, message, details);
136
136
  }
137
137
  }
138
138
  exports.ServiceUnavailableError = ServiceUnavailableError;
@@ -144,8 +144,8 @@ exports.ServiceUnavailableError = ServiceUnavailableError;
144
144
  * prompt rather than treating this as a generic auth failure.
145
145
  */
146
146
  class AccountPendingDeletionError extends app_error_1.AppError {
147
- constructor(message = "This account is scheduled for deletion. Recover it within the 30-day window.", details) {
148
- super("ACCOUNT_PENDING_DELETION", 403, message, details);
147
+ constructor(message = 'This account is scheduled for deletion. Recover it within the 30-day window.', details) {
148
+ super('ACCOUNT_PENDING_DELETION', 403, message, details);
149
149
  }
150
150
  }
151
151
  exports.AccountPendingDeletionError = AccountPendingDeletionError;
@@ -157,8 +157,8 @@ exports.AccountPendingDeletionError = AccountPendingDeletionError;
157
157
  * prompt the user to restore the organization instead.
158
158
  */
159
159
  class OrganizationPendingDeletionError extends app_error_1.AppError {
160
- constructor(message = "This organization is scheduled for deletion. Restore it within the 30-day window.", details) {
161
- super("ORGANIZATION_PENDING_DELETION", 403, message, details);
160
+ constructor(message = 'This organization is scheduled for deletion. Restore it within the 30-day window.', details) {
161
+ super('ORGANIZATION_PENDING_DELETION', 403, message, details);
162
162
  }
163
163
  }
164
164
  exports.OrganizationPendingDeletionError = OrganizationPendingDeletionError;
@@ -170,8 +170,8 @@ exports.OrganizationPendingDeletionError = OrganizationPendingDeletionError;
170
170
  * prompt the user to restore the workspace instead.
171
171
  */
172
172
  class WorkspacePendingDeletionError extends app_error_1.AppError {
173
- constructor(message = "This workspace is scheduled for deletion. Restore it within the 30-day window.", details) {
174
- super("WORKSPACE_PENDING_DELETION", 403, message, details);
173
+ constructor(message = 'This workspace is scheduled for deletion. Restore it within the 30-day window.', details) {
174
+ super('WORKSPACE_PENDING_DELETION', 403, message, details);
175
175
  }
176
176
  }
177
177
  exports.WorkspacePendingDeletionError = WorkspacePendingDeletionError;
@@ -1,2 +1,2 @@
1
- export * from "./app-error";
2
- export * from "./http-errors";
1
+ export * from './app-error';
2
+ export * from './http-errors';
@@ -1,2 +1,2 @@
1
- export * from "./service-client";
2
- export * from "./request-context";
1
+ export * from './service-client';
2
+ export * from './request-context';
@@ -1,5 +1,5 @@
1
- import { Request } from "express";
2
- import { UserContext, MachineContext, RequestMetadataContext } from "../types";
1
+ import { Request } from 'express';
2
+ import { UserContext, MachineContext, RequestMetadataContext } from '../types';
3
3
  export declare function getUserContext(req: Request): UserContext;
4
4
  export declare function getMachineContext(req: Request): MachineContext;
5
5
  export declare function getRequestContext(req: Request): RequestMetadataContext;
@@ -8,21 +8,21 @@ exports.getValidatedParams = getValidatedParams;
8
8
  exports.getValidatedQuery = getValidatedQuery;
9
9
  function getUserContext(req) {
10
10
  const ctx = req.authContext;
11
- if (!ctx || ctx.type !== "user") {
12
- throw new Error("User context missing. Did you forget requireUser?");
11
+ if (!ctx || ctx.type !== 'user') {
12
+ throw new Error('User context missing. Did you forget requireUser?');
13
13
  }
14
14
  return ctx;
15
15
  }
16
16
  function getMachineContext(req) {
17
17
  const ctx = req.authContext;
18
- if (!ctx || ctx.type !== "machine") {
19
- throw new Error("Machine context missing. Did you forget requireMachine?");
18
+ if (!ctx || ctx.type !== 'machine') {
19
+ throw new Error('Machine context missing. Did you forget requireMachine?');
20
20
  }
21
21
  return ctx;
22
22
  }
23
23
  function getRequestContext(req) {
24
24
  if (!req.requestContext) {
25
- throw new Error("Request context missing. Did you forget requestContext middleware?");
25
+ throw new Error('Request context missing. Did you forget requestContext middleware?');
26
26
  }
27
27
  return req.requestContext;
28
28
  }
@@ -4,8 +4,8 @@
4
4
  * Handles automatic JWT forwarding, request correlation tracking via header propagation,
5
5
  * and automatic retry behaviors on transient failures.
6
6
  */
7
- import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
8
- import { Request } from "express";
7
+ import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
8
+ import { Request } from 'express';
9
9
  /**
10
10
  * ServiceClient encapsulates an AxiosInstance configured for service-to-service HTTP requests.
11
11
  * Supports token forwarding (Pattern 1) and secure internal calls (Pattern 2).
@@ -151,14 +151,14 @@ class ServiceClient {
151
151
  */
152
152
  mergeAuth(req, config) {
153
153
  const authHeader = req.headers.authorization;
154
- const upstream = req.headers["x-request-id"];
154
+ const upstream = req.headers['x-request-id'];
155
155
  const requestId = req.id ?? (Array.isArray(upstream) ? upstream[0] : upstream) ?? `fallback-${Date.now()}`;
156
156
  return {
157
157
  ...config,
158
158
  headers: {
159
159
  ...config?.headers,
160
160
  ...(authHeader ? { Authorization: authHeader } : {}),
161
- "x-request-id": requestId,
161
+ 'x-request-id': requestId,
162
162
  },
163
163
  };
164
164
  }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- export * from "./enums";
2
- export * from "./errors";
3
- export * from "./dtos";
4
- export * from "./authorization";
5
- export * from "./utils";
6
- export * from "./middleware";
7
- export * from "./types";
8
- export * from "./jwt";
9
- export * from "./http";
10
- export * from "./messaging";
1
+ export * from './enums';
2
+ export * from './errors';
3
+ export * from './dtos';
4
+ export * from './contracts';
5
+ export * from './authorization';
6
+ export * from './utils';
7
+ export * from './middleware';
8
+ export * from './types';
9
+ export * from './jwt';
10
+ export * from './http';
11
+ export * from './messaging';
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./enums"), exports);
18
18
  __exportStar(require("./errors"), exports);
19
19
  __exportStar(require("./dtos"), exports);
20
+ __exportStar(require("./contracts"), exports);
20
21
  __exportStar(require("./authorization"), exports);
21
22
  __exportStar(require("./utils"), exports);
22
23
  __exportStar(require("./middleware"), exports);
@@ -1,3 +1,3 @@
1
- export * from "./user-jwt-verifier";
2
- export * from "./machine-jwt-verifier";
3
- export * from "./machine-token-client";
1
+ export * from './user-jwt-verifier';
2
+ export * from './machine-jwt-verifier';
3
+ export * from './machine-token-client';
@@ -2,7 +2,7 @@
2
2
  * @file machine-jwt-verifier.ts
3
3
  * @description Verifies service-to-service RS256 machine JWTs issued by auth-service.
4
4
  */
5
- import { MachineTokenPayload } from "../types";
5
+ import { MachineTokenPayload } from '../types';
6
6
  /**
7
7
  * Verifies auth-service issued machine tokens used for service-to-service calls.
8
8
  */
@@ -39,19 +39,18 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.MachineJwtVerifier = void 0;
41
41
  const jose = __importStar(require("jose"));
42
- const CLOCK_TOLERANCE_SECONDS = 30;
43
- const JWKS_CACHE_MAX_AGE_MS = 10 * 60 * 1000;
44
- const JWKS_FETCH_TIMEOUT_MS = 5000;
42
+ const env_util_1 = require("../utils/env.util");
43
+ const CLOCK_TOLERANCE_SECONDS = Number((0, env_util_1.getEnv)('JWT_CLOCK_TOLERANCE_SECONDS'));
44
+ const JWKS_CACHE_MAX_AGE_MS = Number((0, env_util_1.getEnv)('JWKS_CACHE_MAX_AGE_MS'));
45
+ const JWKS_FETCH_TIMEOUT_MS = Number((0, env_util_1.getEnv)('JWKS_FETCH_TIMEOUT_MS'));
45
46
  /**
46
47
  * Verifies auth-service issued machine tokens used for service-to-service calls.
47
48
  */
48
49
  class MachineJwtVerifier {
49
50
  constructor(authServiceJwksUri) {
50
- const jwksUri = authServiceJwksUri ??
51
- process.env.AUTH_JWKS_URI ??
52
- "http://auth-service:3001/.well-known/jwks.json";
53
- this.issuer = process.env.MACHINE_JWT_ISSUER ?? "discover-cloud:auth-service";
54
- this.audience = process.env.MACHINE_JWT_AUDIENCE ?? "discover-cloud:services";
51
+ const jwksUri = authServiceJwksUri ?? (0, env_util_1.getEnv)('AUTH_JWKS_URI');
52
+ this.issuer = (0, env_util_1.getEnv)('MACHINE_JWT_ISSUER');
53
+ this.audience = (0, env_util_1.getEnv)('MACHINE_JWT_AUDIENCE');
55
54
  this.jwks = jose.createRemoteJWKSet(new URL(jwksUri), {
56
55
  cacheMaxAge: JWKS_CACHE_MAX_AGE_MS,
57
56
  timeoutDuration: JWKS_FETCH_TIMEOUT_MS,
@@ -67,16 +66,16 @@ class MachineJwtVerifier {
67
66
  const { payload } = await jose.jwtVerify(token, this.jwks, {
68
67
  issuer: this.issuer,
69
68
  audience: this.audience,
70
- algorithms: ["RS256"],
69
+ algorithms: ['RS256'],
71
70
  clockTolerance: CLOCK_TOLERANCE_SECONDS,
72
71
  });
73
- if (payload.typ !== "machine") {
74
- throw new jose.errors.JWTClaimValidationFailed(`Token type mismatch: expected "machine", got "${String(payload.typ)}"`, payload, "typ", "check_failed");
72
+ if (payload.typ !== 'machine') {
73
+ throw new jose.errors.JWTClaimValidationFailed(`Token type mismatch: expected "machine", got "${String(payload.typ)}"`, payload, 'typ', 'check_failed');
75
74
  }
76
- if (typeof payload.serviceId !== "string" ||
77
- typeof payload.jti !== "string" ||
75
+ if (typeof payload.serviceId !== 'string' ||
76
+ typeof payload.jti !== 'string' ||
78
77
  !Array.isArray(payload.scopes)) {
79
- throw new jose.errors.JWTClaimValidationFailed("Missing required machine token claims", payload, "payload", "missing");
78
+ throw new jose.errors.JWTClaimValidationFailed('Missing required machine token claims', payload, 'payload', 'missing');
80
79
  }
81
80
  return payload;
82
81
  }
@@ -3,7 +3,7 @@
3
3
  * @description HTTP client for fetching and caching short-lived machine JWTs
4
4
  * from the Auth Service for service-to-service communication.
5
5
  */
6
- import { ILogger } from "../utils";
6
+ import { ILogger } from '../utils';
7
7
  /**
8
8
  * Handles retrieving and caching auth-service issued machine JWTs.
9
9
  */
@@ -66,12 +66,12 @@ class MachineTokenClient {
66
66
  * @returns {Promise<string>} The freshly issued machine token.
67
67
  */
68
68
  async fetchToken(requestId) {
69
- const url = `${this.authServiceUrl.replace(/\/$/, "")}/internal/machine-token`;
70
- this.logger.debug({ serviceId: this.serviceId, requestId }, "Fetching machine token");
69
+ const url = `${this.authServiceUrl.replace(/\/$/, '')}/internal/machine-token`;
70
+ this.logger.debug({ serviceId: this.serviceId, requestId }, 'Fetching machine token');
71
71
  const response = await fetch(url, {
72
- method: "POST",
72
+ method: 'POST',
73
73
  headers: {
74
- "content-type": "application/json",
74
+ 'content-type': 'application/json',
75
75
  },
76
76
  body: JSON.stringify({
77
77
  serviceId: this.serviceId,
@@ -80,15 +80,15 @@ class MachineTokenClient {
80
80
  }),
81
81
  });
82
82
  if (!response.ok) {
83
- const text = await response.text().catch(() => "(unreadable)");
83
+ const text = await response.text().catch(() => '(unreadable)');
84
84
  throw new Error(`Machine token request failed (${response.status}): ${text}`);
85
85
  }
86
86
  const body = (await response.json());
87
87
  if (!body.success ||
88
88
  !body.data?.accessToken ||
89
- typeof body.data.expiresIn !== "number" ||
89
+ typeof body.data.expiresIn !== 'number' ||
90
90
  body.data.expiresIn <= 0) {
91
- throw new Error("Invalid machine token response");
91
+ throw new Error('Invalid machine token response');
92
92
  }
93
93
  const now = Math.floor(Date.now() / 1000);
94
94
  this.cache = {
@@ -98,7 +98,7 @@ class MachineTokenClient {
98
98
  this.logger.debug({
99
99
  serviceId: this.serviceId,
100
100
  expiresAt: this.cache.expiresAt,
101
- }, "Machine token cached");
101
+ }, 'Machine token cached');
102
102
  return this.cache.token;
103
103
  }
104
104
  }
@@ -2,7 +2,7 @@
2
2
  * @file user-jwt-verifier.ts
3
3
  * @description Verifies user RS256 access tokens issued by auth-service.
4
4
  */
5
- import { AccessTokenPayload, UserContext } from "../types";
5
+ import { AccessTokenPayload, UserContext } from '../types';
6
6
  /**
7
7
  * Verifies auth-service issued user access tokens.
8
8
  */
@@ -39,17 +39,18 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.UserJwtVerifier = void 0;
41
41
  const jose = __importStar(require("jose"));
42
- const CLOCK_TOLERANCE_SECONDS = 30;
43
- const JWKS_CACHE_MAX_AGE_MS = 10 * 60 * 1000;
44
- const JWKS_FETCH_TIMEOUT_MS = 5000;
42
+ const env_util_1 = require("../utils/env.util");
43
+ const CLOCK_TOLERANCE_SECONDS = Number((0, env_util_1.getEnv)('JWT_CLOCK_TOLERANCE_SECONDS'));
44
+ const JWKS_CACHE_MAX_AGE_MS = Number((0, env_util_1.getEnv)('JWKS_CACHE_MAX_AGE_MS'));
45
+ const JWKS_FETCH_TIMEOUT_MS = Number((0, env_util_1.getEnv)('JWKS_FETCH_TIMEOUT_MS'));
45
46
  /**
46
47
  * Verifies auth-service issued user access tokens.
47
48
  */
48
49
  class UserJwtVerifier {
49
50
  constructor() {
50
- const authJwksUri = process.env.AUTH_JWKS_URI ?? "http://auth-service:3001/.well-known/jwks.json";
51
- this.issuer = process.env.EXTERNAL_JWT_ISSUER ?? "discover-cloud:auth-service";
52
- this.audience = process.env.EXTERNAL_JWT_AUDIENCE ?? "discover-cloud:api";
51
+ const authJwksUri = (0, env_util_1.getEnv)('AUTH_JWKS_URI');
52
+ this.issuer = (0, env_util_1.getEnv)('EXTERNAL_JWT_ISSUER');
53
+ this.audience = (0, env_util_1.getEnv)('EXTERNAL_JWT_AUDIENCE');
53
54
  this.jwks = jose.createRemoteJWKSet(new URL(authJwksUri), {
54
55
  cacheMaxAge: JWKS_CACHE_MAX_AGE_MS,
55
56
  timeoutDuration: JWKS_FETCH_TIMEOUT_MS,
@@ -65,18 +66,18 @@ class UserJwtVerifier {
65
66
  const { payload } = await jose.jwtVerify(token, this.jwks, {
66
67
  issuer: this.issuer,
67
68
  audience: this.audience,
68
- algorithms: ["RS256"],
69
+ algorithms: ['RS256'],
69
70
  clockTolerance: CLOCK_TOLERANCE_SECONDS,
70
71
  });
71
- if (payload.typ !== "access") {
72
- throw new jose.errors.JWTClaimValidationFailed(`Token type mismatch: expected "access", got "${String(payload.typ)}"`, payload, "typ", "check_failed");
72
+ if (payload.typ !== 'access') {
73
+ throw new jose.errors.JWTClaimValidationFailed(`Token type mismatch: expected "access", got "${String(payload.typ)}"`, payload, 'typ', 'check_failed');
73
74
  }
74
- if (typeof payload.accountId !== "string" ||
75
- typeof payload.sessionId !== "string" ||
76
- typeof payload.jti !== "string" ||
77
- typeof payload.iat !== "number" ||
78
- typeof payload.exp !== "number") {
79
- throw new jose.errors.JWTClaimValidationFailed("Missing required access token claims", payload, "payload", "missing");
75
+ if (typeof payload.accountId !== 'string' ||
76
+ typeof payload.sessionId !== 'string' ||
77
+ typeof payload.jti !== 'string' ||
78
+ typeof payload.iat !== 'number' ||
79
+ typeof payload.exp !== 'number') {
80
+ throw new jose.errors.JWTClaimValidationFailed('Missing required access token claims', payload, 'payload', 'missing');
80
81
  }
81
82
  return payload;
82
83
  }
@@ -91,7 +92,7 @@ class UserJwtVerifier {
91
92
  return {
92
93
  payload,
93
94
  context: {
94
- type: "user",
95
+ type: 'user',
95
96
  accountId: payload.accountId,
96
97
  sessionId: payload.sessionId,
97
98
  },
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @file audit.publisher.ts
3
+ * @description Publishes audit events with automatic request context enrichment.
4
+ */
5
+ import { RabbitMQClient } from '../messaging';
6
+ import { RecordAuditActionInput } from '../dtos';
7
+ import { ILogger } from '../utils/logger.util';
8
+ export type PublishAuditInput = Omit<RecordAuditActionInput, 'actorId' | 'actorType' | 'requestId'> & Partial<Pick<RecordAuditActionInput, 'actorId' | 'actorType' | 'requestId'>>;
9
+ export declare class AuditPublisher {
10
+ private readonly mq;
11
+ private readonly logger;
12
+ constructor(mq: RabbitMQClient, logger: ILogger);
13
+ publish(input: PublishAuditInput): Promise<void>;
14
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * @file audit.publisher.ts
4
+ * @description Publishes audit events with automatic request context enrichment.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.AuditPublisher = void 0;
8
+ const dtos_1 = require("../dtos");
9
+ const request_context_storage_1 = require("../context/request-context.storage");
10
+ class AuditPublisher {
11
+ constructor(mq, logger) {
12
+ this.mq = mq;
13
+ this.logger = logger;
14
+ }
15
+ async publish(input) {
16
+ const ctx = request_context_storage_1.RequestContextProvider.get();
17
+ let actorType = dtos_1.AuditActorType.SYSTEM;
18
+ let actorId = null;
19
+ if (ctx?.actor) {
20
+ switch (ctx.actor.type) {
21
+ case 'user':
22
+ actorType = dtos_1.AuditActorType.USER;
23
+ actorId = ctx.actor.accountId;
24
+ break;
25
+ case 'machine':
26
+ actorType = dtos_1.AuditActorType.SERVICE;
27
+ actorId = ctx.actor.serviceId;
28
+ break;
29
+ }
30
+ }
31
+ const payload = {
32
+ severity: dtos_1.AuditSeverity.INFO,
33
+ timestamp: new Date().toISOString(),
34
+ actorType,
35
+ actorId,
36
+ requestId: ctx?.requestId ?? null,
37
+ ipAddress: ctx?.ipAddress ?? null,
38
+ userAgent: ctx?.userAgent ?? null,
39
+ ...input,
40
+ };
41
+ try {
42
+ await this.mq.publishEvent(`audit.${input.action}`, payload);
43
+ }
44
+ catch (err) {
45
+ this.logger.error({ err, action: input.action }, 'Failed to publish audit event');
46
+ }
47
+ }
48
+ }
49
+ exports.AuditPublisher = AuditPublisher;
@@ -1 +1,2 @@
1
1
  export * from './rabbitmq.client';
2
+ export * from './audit.publisher';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./rabbitmq.client"), exports);
18
+ __exportStar(require("./audit.publisher"), exports);
@@ -1,13 +1,25 @@
1
+ import { ILogger } from '../utils/logger.util';
1
2
  export declare class RabbitMQClient {
2
3
  private connection;
3
4
  private channel;
4
- private url;
5
- private exchangeName;
6
- constructor(url: string);
5
+ private readonly url;
6
+ private readonly exchangeName;
7
+ private readonly logger;
8
+ private reconnecting;
9
+ constructor(url: string, logger?: ILogger);
10
+ /**
11
+ * Establishes a connection to RabbitMQ.
12
+ * Blocks until a connection is successfully established.
13
+ */
7
14
  connect(): Promise<void>;
8
- publishEvent(routingKey: string, payload: any): Promise<void>;
9
- subscribe(queueName: string, routingKey: string, handler: (payload: any) => Promise<void>, options?: {
15
+ /**
16
+ * Background reconnect loop.
17
+ */
18
+ private reconnect;
19
+ publishEvent(routingKey: string, payload: unknown): Promise<void>;
20
+ subscribe(queueName: string, routingKey: string, handler: (payload: unknown) => Promise<void>, options?: {
10
21
  prefetch?: number;
11
22
  }): Promise<void>;
12
23
  close(): Promise<void>;
24
+ private delay;
13
25
  }