@discover-cloud/shared 1.2.9 → 1.3.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 (165) hide show
  1. package/dist/authorization/index.d.ts +1 -1
  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 +156 -143
  5. package/dist/authorization/permission-checker.d.ts +30 -0
  6. package/dist/authorization/permission-checker.js +40 -0
  7. package/dist/contracts/auth-service/account.dto.d.ts +30 -0
  8. package/dist/contracts/auth-service/account.dto.js +7 -0
  9. package/dist/contracts/auth-service/auth.dto.d.ts +21 -0
  10. package/dist/contracts/auth-service/auth.dto.js +10 -0
  11. package/dist/dtos/auth-service.dto.d.ts +72 -26
  12. package/dist/dtos/auth-service.dto.js +4 -0
  13. package/dist/dtos/cloud-service.dto.d.ts +51 -27
  14. package/dist/dtos/cloud-service.dto.js +5 -0
  15. package/dist/dtos/insights-service.dto.d.ts +18 -27
  16. package/dist/dtos/insights-service.dto.js +8 -0
  17. package/dist/dtos/response.dto.d.ts +7 -84
  18. package/dist/dtos/response.dto.js +2 -21
  19. package/dist/dtos/user-service.dto.d.ts +118 -13
  20. package/dist/enums/domain.enums.d.ts +246 -75
  21. package/dist/enums/domain.enums.js +305 -102
  22. package/dist/enums/permissions.enums.d.ts +142 -80
  23. package/dist/enums/permissions.enums.js +174 -133
  24. package/dist/errors/app-error.d.ts +13 -18
  25. package/dist/errors/app-error.js +14 -20
  26. package/dist/errors/http-errors.d.ts +105 -17
  27. package/dist/errors/http-errors.js +118 -33
  28. package/dist/http/index.d.ts +1 -0
  29. package/dist/http/index.js +1 -0
  30. package/dist/http/request-context.d.ts +8 -0
  31. package/dist/http/request-context.js +37 -0
  32. package/dist/http/service-client.d.ts +91 -38
  33. package/dist/http/service-client.js +102 -53
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +1 -0
  36. package/dist/jwt/index.d.ts +2 -1
  37. package/dist/jwt/index.js +2 -1
  38. package/dist/jwt/machine-jwt-verifier.d.ts +21 -0
  39. package/dist/jwt/machine-jwt-verifier.js +84 -0
  40. package/dist/jwt/machine-token-client.d.ts +35 -4
  41. package/dist/jwt/machine-token-client.js +57 -23
  42. package/dist/jwt/user-jwt-verifier.d.ts +31 -0
  43. package/dist/jwt/user-jwt-verifier.js +101 -0
  44. package/dist/messaging/index.d.ts +1 -0
  45. package/dist/{security → messaging}/index.js +1 -1
  46. package/dist/messaging/rabbitmq.client.d.ts +13 -0
  47. package/dist/messaging/rabbitmq.client.js +127 -0
  48. package/dist/middleware/error-handler.middleware.d.ts +8 -28
  49. package/dist/middleware/error-handler.middleware.js +61 -52
  50. package/dist/middleware/index.d.ts +7 -5
  51. package/dist/middleware/index.js +7 -5
  52. package/dist/middleware/request-context.middleware.d.ts +17 -0
  53. package/dist/middleware/request-context.middleware.js +73 -0
  54. package/dist/middleware/require-auth.middleware.d.ts +17 -70
  55. package/dist/middleware/require-auth.middleware.js +24 -103
  56. package/dist/middleware/require-machine.middleware.d.ts +38 -0
  57. package/dist/middleware/require-machine.middleware.js +123 -0
  58. package/dist/middleware/require-org-permission-from-body.middleware.d.ts +10 -0
  59. package/dist/middleware/require-org-permission-from-body.middleware.js +24 -0
  60. package/dist/middleware/require-org-permission.middleware.d.ts +19 -0
  61. package/dist/middleware/require-org-permission.middleware.js +115 -0
  62. package/dist/middleware/require-platform-permission.middleware.d.ts +19 -0
  63. package/dist/middleware/require-platform-permission.middleware.js +91 -0
  64. package/dist/middleware/require-user.middleware.d.ts +14 -0
  65. package/dist/middleware/require-user.middleware.js +30 -0
  66. package/dist/middleware/require-workspace-permission.middleware.d.ts +20 -0
  67. package/dist/middleware/require-workspace-permission.middleware.js +114 -0
  68. package/dist/middleware/validate.middleware.d.ts +29 -29
  69. package/dist/middleware/validate.middleware.js +30 -32
  70. package/dist/types/express.types.d.ts +83 -122
  71. package/dist/types/express.types.js +28 -47
  72. package/dist/utils/date.util.d.ts +6 -0
  73. package/dist/utils/date.util.js +11 -0
  74. package/dist/utils/env.util.d.ts +6 -0
  75. package/dist/utils/env.util.js +19 -0
  76. package/dist/utils/index.d.ts +5 -4
  77. package/dist/utils/index.js +5 -4
  78. package/dist/utils/logger.util.d.ts +26 -0
  79. package/dist/utils/logger.util.js +53 -0
  80. package/dist/utils/pagination.util.d.ts +6 -0
  81. package/dist/utils/pagination.util.js +20 -0
  82. package/dist/utils/response.util.d.ts +27 -0
  83. package/dist/utils/response.util.js +56 -0
  84. package/dist/utils/slug.util.d.ts +9 -0
  85. package/dist/utils/slug.util.js +32 -0
  86. package/dist/utils/url-safety.util.d.ts +6 -0
  87. package/dist/utils/url-safety.util.js +72 -0
  88. package/package.json +3 -1
  89. package/dist/authorization/permissions.d.ts +0 -78
  90. package/dist/authorization/permissions.js +0 -174
  91. package/dist/context/access-context.d.ts +0 -10
  92. package/dist/context/access-context.js +0 -2
  93. package/dist/context/index.d.ts +0 -1
  94. package/dist/context/index.js +0 -17
  95. package/dist/dto/auth-service.dtos.d.ts +0 -44
  96. package/dist/dto/auth-service.dtos.js +0 -2
  97. package/dist/dto/index.d.ts +0 -3
  98. package/dist/dto/index.js +0 -19
  99. package/dist/dto/response.dtos.d.ts +0 -55
  100. package/dist/dto/response.dtos.js +0 -6
  101. package/dist/dto/user-service.dtos.d.ts +0 -50
  102. package/dist/dto/user-service.dtos.js +0 -2
  103. package/dist/enums/auth-service.enums.d.ts +0 -12
  104. package/dist/enums/auth-service.enums.js +0 -17
  105. package/dist/enums/permissions.types.d.ts +0 -12
  106. package/dist/enums/permissions.types.js +0 -17
  107. package/dist/enums/user-service.enums.d.ts +0 -32
  108. package/dist/enums/user-service.enums.js +0 -41
  109. package/dist/internal/index.d.ts +0 -4
  110. package/dist/internal/index.js +0 -20
  111. package/dist/internal/internal-jwt.service.d.ts +0 -13
  112. package/dist/internal/internal-jwt.service.js +0 -88
  113. package/dist/internal/internal-jwt.types.d.ts +0 -7
  114. package/dist/internal/internal-jwt.types.js +0 -2
  115. package/dist/internal/internal-key-manager.d.ts +0 -16
  116. package/dist/internal/internal-key-manager.js +0 -67
  117. package/dist/internal/registry.d.ts +0 -8
  118. package/dist/internal/registry.js +0 -34
  119. package/dist/internal/service-client.d.ts +0 -9
  120. package/dist/internal/service-client.js +0 -94
  121. package/dist/jwt/internal-jwt-verifier.d.ts +0 -41
  122. package/dist/jwt/internal-jwt-verifier.js +0 -185
  123. package/dist/jwt/jwt-verifier.d.ts +0 -9
  124. package/dist/jwt/jwt-verifier.js +0 -36
  125. package/dist/jwt/service-client.d.ts +0 -7
  126. package/dist/jwt/service-client.js +0 -87
  127. package/dist/middleware/authorize.d.ts +0 -3
  128. package/dist/middleware/authorize.js +0 -24
  129. package/dist/middleware/authorize.middleware.d.ts +0 -54
  130. package/dist/middleware/authorize.middleware.js +0 -104
  131. package/dist/middleware/error-handler.d.ts +0 -4
  132. package/dist/middleware/error-handler.js +0 -23
  133. package/dist/middleware/request-id.d.ts +0 -2
  134. package/dist/middleware/request-id.js +0 -9
  135. package/dist/middleware/request-id.middleware.d.ts +0 -22
  136. package/dist/middleware/request-id.middleware.js +0 -34
  137. package/dist/middleware/require-auth.d.ts +0 -10
  138. package/dist/middleware/require-auth.js +0 -34
  139. package/dist/middleware/require-human.middleware.d.ts +0 -2
  140. package/dist/middleware/require-human.middleware.js +0 -18
  141. package/dist/middleware/require-internal.middleware.d.ts +0 -18
  142. package/dist/middleware/require-internal.middleware.js +0 -183
  143. package/dist/middleware/validate.d.ts +0 -5
  144. package/dist/middleware/validate.js +0 -18
  145. package/dist/middleware/validated-merge.middleware.d.ts +0 -20
  146. package/dist/middleware/validated-merge.middleware.js +0 -33
  147. package/dist/middleware/verify-internal-jwt.d.ts +0 -7
  148. package/dist/middleware/verify-internal-jwt.js +0 -25
  149. package/dist/security/guard.d.ts +0 -10
  150. package/dist/security/guard.js +0 -40
  151. package/dist/security/index.d.ts +0 -1
  152. package/dist/types/express.d.ts +0 -22
  153. package/dist/types/express.js +0 -3
  154. package/dist/utils/date.utils.d.ts +0 -25
  155. package/dist/utils/date.utils.js +0 -30
  156. package/dist/utils/env.d.ts +0 -46
  157. package/dist/utils/env.js +0 -61
  158. package/dist/utils/env.utils.d.ts +0 -46
  159. package/dist/utils/env.utils.js +0 -61
  160. package/dist/utils/logger.utils.d.ts +0 -66
  161. package/dist/utils/logger.utils.js +0 -97
  162. package/dist/utils/response.d.ts +0 -4
  163. package/dist/utils/response.js +0 -35
  164. package/dist/utils/response.utils.d.ts +0 -54
  165. package/dist/utils/response.utils.js +0 -85
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @file require-workspace-permission.middleware.ts
3
+ * @description Express middleware factory to enforce workspace-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 requireWorkspacePermission middleware factory.
10
+ *
11
+ * @param {PermissionCacheService} permissionCache - Shared Redis cache service.
12
+ * @param {(accountId: string, workspaceId: string) => Promise<string[]>} resolveFn
13
+ * - Function called on a cache miss to fetch the caller's permission strings for the workspace.
14
+ * @returns {object} Workspace permission middlewares.
15
+ */
16
+ export declare const createRequireWorkspacePermission: (permissionCache: PermissionCacheService, resolveFn: (accountId: string, workspaceId: string) => Promise<string[]>) => {
17
+ requireWorkspacePermission: (permission: Permission) => RequestHandler;
18
+ requireAnyWorkspacePermission: (...permissions: Permission[]) => RequestHandler;
19
+ requireAllWorkspacePermissions: (...permissions: Permission[]) => RequestHandler;
20
+ };
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * @file require-workspace-permission.middleware.ts
4
+ * @description Express middleware factory to enforce workspace-scoped permission checks.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createRequireWorkspacePermission = void 0;
8
+ const permission_checker_1 = require("../authorization/permission-checker");
9
+ const utils_1 = require("../utils");
10
+ /**
11
+ * Creates requireWorkspacePermission middleware factory.
12
+ *
13
+ * @param {PermissionCacheService} permissionCache - Shared Redis cache service.
14
+ * @param {(accountId: string, workspaceId: string) => Promise<string[]>} resolveFn
15
+ * - Function called on a cache miss to fetch the caller's permission strings for the workspace.
16
+ * @returns {object} Workspace permission middlewares.
17
+ */
18
+ const createRequireWorkspacePermission = (permissionCache, resolveFn) => {
19
+ const requireWorkspacePermission = (permission) => {
20
+ return async (req, res, next) => {
21
+ const ctx = req.authContext;
22
+ if (!ctx || ctx.type !== "user") {
23
+ (0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
24
+ return;
25
+ }
26
+ // Resolve workspaceId from request locations
27
+ const workspaceId = req.params["workspaceId"] ??
28
+ req.validated?.params?.["workspaceId"] ??
29
+ req.validated?.body?.["workspaceId"];
30
+ if (!workspaceId || typeof workspaceId !== "string") {
31
+ (0, utils_1.failure)(res, req, "Bad Request: Missing workspaceId", "BAD_REQUEST", 400);
32
+ return;
33
+ }
34
+ try {
35
+ const perms = await permissionCache.resolveWorkspacePermissions(ctx.accountId, workspaceId, async () => {
36
+ return resolveFn(ctx.accountId, workspaceId);
37
+ });
38
+ if (!permission_checker_1.PermissionChecker.has(perms, permission)) {
39
+ (0, utils_1.failure)(res, req, `Forbidden: Missing required workspace permission: ${permission}`, "FORBIDDEN", 403);
40
+ return;
41
+ }
42
+ next();
43
+ }
44
+ catch (err) {
45
+ next(err);
46
+ }
47
+ };
48
+ };
49
+ const requireAnyWorkspacePermission = (...permissions) => {
50
+ return async (req, res, next) => {
51
+ const ctx = req.authContext;
52
+ if (!ctx || ctx.type !== "user") {
53
+ (0, utils_1.failure)(res, req, "Unauthorized: No access context", "UNAUTHORIZED", 401);
54
+ return;
55
+ }
56
+ const workspaceId = req.params["workspaceId"] ??
57
+ req.validated?.params?.["workspaceId"] ??
58
+ req.validated?.body?.["workspaceId"];
59
+ if (!workspaceId || typeof workspaceId !== "string") {
60
+ (0, utils_1.failure)(res, req, "Bad Request: Missing workspaceId", "BAD_REQUEST", 400);
61
+ return;
62
+ }
63
+ try {
64
+ const perms = await permissionCache.resolveWorkspacePermissions(ctx.accountId, workspaceId, async () => {
65
+ return resolveFn(ctx.accountId, workspaceId);
66
+ });
67
+ if (!permission_checker_1.PermissionChecker.hasAny(perms, permissions)) {
68
+ (0, utils_1.failure)(res, req, `Forbidden: Missing one of required workspace permissions: [${permissions.join(", ")}]`, "FORBIDDEN", 403);
69
+ return;
70
+ }
71
+ next();
72
+ }
73
+ catch (err) {
74
+ next(err);
75
+ }
76
+ };
77
+ };
78
+ const requireAllWorkspacePermissions = (...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 access context", "UNAUTHORIZED", 401);
83
+ return;
84
+ }
85
+ const workspaceId = req.params["workspaceId"] ??
86
+ req.validated?.params?.["workspaceId"] ??
87
+ req.validated?.body?.["workspaceId"];
88
+ if (!workspaceId || typeof workspaceId !== "string") {
89
+ (0, utils_1.failure)(res, req, "Bad Request: Missing workspaceId", "BAD_REQUEST", 400);
90
+ return;
91
+ }
92
+ try {
93
+ const perms = await permissionCache.resolveWorkspacePermissions(ctx.accountId, workspaceId, async () => {
94
+ return resolveFn(ctx.accountId, workspaceId);
95
+ });
96
+ if (!permission_checker_1.PermissionChecker.hasAll(perms, permissions)) {
97
+ const missing = permissions.filter(p => !permission_checker_1.PermissionChecker.has(perms, p));
98
+ (0, utils_1.failure)(res, req, `Forbidden: Missing required workspace permissions: [${missing.join(", ")}]`, "FORBIDDEN", 403);
99
+ return;
100
+ }
101
+ next();
102
+ }
103
+ catch (err) {
104
+ next(err);
105
+ }
106
+ };
107
+ };
108
+ return {
109
+ requireWorkspacePermission,
110
+ requireAnyWorkspacePermission,
111
+ requireAllWorkspacePermissions,
112
+ };
113
+ };
114
+ exports.createRequireWorkspacePermission = createRequireWorkspacePermission;
@@ -1,35 +1,35 @@
1
+ /**
2
+ * @file validate.middleware.ts
3
+ * @description Request validation middleware leveraging Zod schema definitions.
4
+ * Supports validating multiple request sources without overwriting previous results.
5
+ */
1
6
  import { Request, Response, NextFunction } from "express";
2
7
  import { ZodType } from "zod";
3
8
  /**
4
- * VALIDATOR MIDDLEWARE (@discover-cloud/shared)
5
- * ────────────────────────────────────────────────
6
- * Validates and parses request input against a Zod schema.
7
- * On success, attaches the parsed (coerced + stripped) data to req.validated.
8
- * On failure, passes the ZodError to next() → GlobalErrorHandler → 400.
9
- *
10
- * Source options:
11
- * "body" — req.body (POST/PUT/PATCH payloads)
12
- * "params" — req.params (URL path parameters)
13
- * "query" — req.query (URL query string)
14
- * "headers" — req.headers (custom header validation, e.g. API keys)
15
- *
16
- * Narrowing req.validated in a controller:
17
- * req.validated is typed as unknown in express.d.ts — consumers must
18
- * narrow it to the schema's inferred type at the call site:
19
- *
20
- * const body = req.validated as z.infer<typeof CreateOrderSchema>;
21
- *
22
- * This is intentional: the middleware is schema-agnostic and cannot
23
- * carry the generic type through Express's untyped req object.
24
- *
25
- * Usage:
26
- * router.post(
27
- * "/orders",
28
- * requireAuth,
29
- * Validator.validate(CreateOrderSchema, "body"),
30
- * controller.create,
31
- * );
9
+ * Supported Express request validation sources.
10
+ */
11
+ type ValidationSource = "body" | "params" | "query" | "headers";
12
+ /**
13
+ * Validator utility class containing Express middleware generation methods.
32
14
  */
33
15
  export declare class Validator {
34
- static validate<T>(schema: ZodType<T>, source?: "body" | "params" | "query" | "headers"): (req: Request, _res: Response, next: NextFunction) => void;
16
+ /**
17
+ * Generates middleware to parse and validate request parts.
18
+ *
19
+ * Validated data is stored by source:
20
+ *
21
+ * req.validated = {
22
+ * params: {},
23
+ * query: {},
24
+ * body: {},
25
+ * headers: {}
26
+ * }
27
+ *
28
+ * Multiple validators can safely run on the same request.
29
+ *
30
+ * @param {ZodType<T>} schema - Zod validation schema.
31
+ * @param {ValidationSource} source - Request source.
32
+ */
33
+ static validate<T>(schema: ZodType<T>, source?: ValidationSource): (req: Request, _res: Response, next: NextFunction) => void;
35
34
  }
35
+ export {};
@@ -1,48 +1,46 @@
1
1
  "use strict";
2
+ /**
3
+ * @file validate.middleware.ts
4
+ * @description Request validation middleware leveraging Zod schema definitions.
5
+ * Supports validating multiple request sources without overwriting previous results.
6
+ */
2
7
  Object.defineProperty(exports, "__esModule", { value: true });
3
8
  exports.Validator = void 0;
4
9
  /**
5
- * VALIDATOR MIDDLEWARE (@discover-cloud/shared)
6
- * ────────────────────────────────────────────────
7
- * Validates and parses request input against a Zod schema.
8
- * On success, attaches the parsed (coerced + stripped) data to req.validated.
9
- * On failure, passes the ZodError to next() → GlobalErrorHandler → 400.
10
- *
11
- * Source options:
12
- * "body" — req.body (POST/PUT/PATCH payloads)
13
- * "params" — req.params (URL path parameters)
14
- * "query" — req.query (URL query string)
15
- * "headers" — req.headers (custom header validation, e.g. API keys)
16
- *
17
- * Narrowing req.validated in a controller:
18
- * req.validated is typed as unknown in express.d.ts — consumers must
19
- * narrow it to the schema's inferred type at the call site:
20
- *
21
- * const body = req.validated as z.infer<typeof CreateOrderSchema>;
22
- *
23
- * This is intentional: the middleware is schema-agnostic and cannot
24
- * carry the generic type through Express's untyped req object.
25
- *
26
- * Usage:
27
- * router.post(
28
- * "/orders",
29
- * requireAuth,
30
- * Validator.validate(CreateOrderSchema, "body"),
31
- * controller.create,
32
- * );
10
+ * Validator utility class containing Express middleware generation methods.
33
11
  */
34
12
  class Validator {
13
+ /**
14
+ * Generates middleware to parse and validate request parts.
15
+ *
16
+ * Validated data is stored by source:
17
+ *
18
+ * req.validated = {
19
+ * params: {},
20
+ * query: {},
21
+ * body: {},
22
+ * headers: {}
23
+ * }
24
+ *
25
+ * Multiple validators can safely run on the same request.
26
+ *
27
+ * @param {ZodType<T>} schema - Zod validation schema.
28
+ * @param {ValidationSource} source - Request source.
29
+ */
35
30
  static validate(schema, source = "body") {
36
31
  return (req, _res, next) => {
37
32
  const result = schema.safeParse(req[source]);
38
33
  if (!result.success) {
39
- // Pass the ZodError to GlobalErrorHandler — it flattens and returns 400.
40
34
  next(result.error);
41
35
  return;
42
36
  }
43
- // Attach the parsed data (coerced + unknown keys stripped by Zod).
44
- // req.validated is typed as unknown — narrow to your schema type in the controller.
45
- req.validated = result.data;
37
+ req.validated = {
38
+ ...(req.validated ?? {}),
39
+ [source]: {
40
+ ...(req.validated?.[source] ?? {}),
41
+ ...result.data,
42
+ },
43
+ };
46
44
  next();
47
45
  };
48
46
  }
@@ -1,49 +1,10 @@
1
1
  /**
2
- * EXPRESS TYPE AUGMENTATION + JWT PAYLOAD TYPES (@discover-cloud/shared)
3
- * ──────────────────────────────────────────────────────────────────────────
4
- * Single source of truth for everything attached to Express's Request object
5
- * and all internal JWT payload shapes.
6
- *
7
- * Import in each service's entrypoint to activate the augmentation:
8
- * import "@discover-cloud/shared/types/express";
9
- *
10
- * ─── Permission flow ────────────────────────────────────────────────────
11
- * JWT carries: accountId + accountRole (lean payload, no perms)
12
- * Redis derives: GlobalPermission[] (role map on cache miss)
13
- * AccessContext has: perms (resolved by auth middleware)
14
- *
15
- * perms never touch the JWT — they live in the Redis permission cache and
16
- * are resolved into req.accessContext by RequireAuthMiddleware per request.
17
- * ────────────────────────────────────────────────────────────────────────
2
+ * @file express.types.ts
3
+ * @description Express Request type augmentations and authentication context definitions.
18
4
  */
19
5
  import { JWTPayload } from "jose";
20
- import { AccountRole, GlobalPermission } from "../enums";
21
- interface BaseInternalJwtPayload extends JWTPayload {
22
- /**
23
- * JWT ID — required.
24
- * Used for the Redis kill-switch (session revocation / account suspension).
25
- * Overrides JWTPayload's optional jti with a required string.
26
- */
6
+ interface BaseTokenPayload extends JWTPayload {
27
7
  jti: string;
28
- /**
29
- * Token type discriminator — guards against token confusion attacks.
30
- * InternalJwtVerifier rejects any token where typ !== "internal",
31
- * even if it passes signature verification.
32
- */
33
- typ: "internal";
34
- /**
35
- * The entity that last minted or forwarded this token.
36
- * "api-gateway" → direct user-initiated request
37
- * "notification-service" → service-to-service call on behalf of a user
38
- * Used for audit logging and conditional authorization.
39
- */
40
- caller: string;
41
- /**
42
- * Propagated request ID for distributed tracing.
43
- * Ties together logs across the entire service call chain.
44
- * Set by the gateway from the incoming x-request-id header.
45
- */
46
- requestId?: string;
47
8
  iss: string;
48
9
  aud: string | string[];
49
10
  iat: number;
@@ -51,116 +12,116 @@ interface BaseInternalJwtPayload extends JWTPayload {
51
12
  nbf: number;
52
13
  }
53
14
  /**
54
- * HumanInternalJwtPayload
55
- * Issued on behalf of an authenticated user after external JWT verification.
56
- * accountRole is carried so the permission cache can derive perms from
57
- * globalRolePermissions on a cold cache miss — without a DB call.
15
+ * Short-lived user access token.
16
+ *
17
+ * Contains authentication identity only.
18
+ * Authorization is resolved dynamically using RBAC.
19
+ */
20
+ export interface AccessTokenPayload extends BaseTokenPayload {
21
+ typ: "access";
22
+ /** Account identifier. */
23
+ accountId: string;
24
+ /** Active session identifier. */
25
+ sessionId: string;
26
+ /** Token/version invalidation marker. */
27
+ ver: number;
28
+ }
29
+ /**
30
+ * Long-lived refresh token payload.
31
+ *
32
+ * Refresh tokens are only used for rotation.
58
33
  */
59
- export interface HumanInternalJwtPayload extends BaseInternalJwtPayload {
60
- isMachine: false;
34
+ export interface RefreshTokenPayload extends BaseTokenPayload {
35
+ typ: "refresh";
61
36
  accountId: string;
62
- accountRole: AccountRole;
63
- externalJti: string;
64
- externalExp: number;
65
37
  }
66
38
  /**
67
- * MachineInternalJwtPayload
68
- * Issued for service-to-service calls with no user context.
69
- * Intentionally carries no user fields — their absence is enforced by the type.
39
+ * Service-to-service authentication token.
70
40
  */
71
- export interface MachineInternalJwtPayload extends BaseInternalJwtPayload {
72
- isMachine: true;
41
+ export interface MachineTokenPayload extends BaseTokenPayload {
42
+ typ: "machine";
73
43
  serviceId: string;
44
+ scopes: string[];
45
+ requestId?: string;
74
46
  }
75
- /** Full internal JWT payload union — use in verifiers and auth middleware */
76
- export type InternalJwtPayload = HumanInternalJwtPayload | MachineInternalJwtPayload;
47
+ /**
48
+ * Any verified JWT generated by auth-service.
49
+ */
50
+ export type VerifiedTokenPayload = AccessTokenPayload | RefreshTokenPayload | MachineTokenPayload;
51
+ /**
52
+ * Minimal data needed to revoke a JWT.
53
+ */
77
54
  export interface TokenRevocationContext {
78
55
  jti: string;
79
56
  exp: number;
80
57
  }
81
- export interface HumanAccessContext {
82
- kind: "human";
58
+ /**
59
+ * Authenticated user request context.
60
+ *
61
+ * Created from access tokens only.
62
+ */
63
+ export interface UserContext {
64
+ type: "user";
83
65
  accountId: string;
84
- accountRole: AccountRole;
85
- /**
86
- * Resolved from Redis permission cache — NOT from the JWT.
87
- * Always present by the time req.accessContext is populated.
88
- * Invalidated on role change, suspension, and account deletion.
89
- */
90
- perms: GlobalPermission[];
66
+ sessionId: string;
91
67
  }
92
- export interface MachineAccessContext {
93
- kind: "machine";
68
+ /**
69
+ * Authenticated internal service context.
70
+ */
71
+ export interface MachineContext {
72
+ type: "machine";
94
73
  serviceId: string;
74
+ scopes: string[];
75
+ }
76
+ export type AuthContext = UserContext | MachineContext;
77
+ export interface RequestMetadataContext {
78
+ requestId: string;
79
+ ipAddress: string | null;
80
+ userAgent: string | null;
95
81
  }
96
- export type AccessContext = HumanAccessContext | MachineAccessContext;
97
- export declare function isHumanPayload(payload: InternalJwtPayload): payload is HumanInternalJwtPayload;
98
- export declare function isMachinePayload(payload: InternalJwtPayload): payload is MachineInternalJwtPayload;
99
82
  /**
100
- * Runtime assertion for jose.jwtVerify() results.
101
- *
102
- * jose returns a generic JWTPayload because it cannot know
103
- * our custom claims. After verification + this assertion,
104
- * TypeScript safely treats it as MachineInternalJwtPayload.
83
+ * Passed into application services for immutable auditing.
105
84
  */
106
- export declare function assertMachinePayload(payload: JWTPayload): asserts payload is MachineInternalJwtPayload;
107
- export declare function isHumanContext(ctx: AccessContext): ctx is HumanAccessContext;
108
- export declare function isMachineContext(ctx: AccessContext): ctx is MachineAccessContext;
109
- export interface VerifiedAccessPayload {
110
- accountId: string;
111
- accountRole: AccountRole;
112
- typ: "access";
113
- iss: string;
114
- aud: string | string[];
115
- iat: number;
116
- exp: number;
117
- nbf: number;
118
- jti: string;
85
+ export interface AuditContext extends Partial<RequestMetadataContext> {
86
+ actorId: string | null;
119
87
  }
120
- export interface VerifiedRefreshPayload {
121
- accountId: string;
122
- typ: "refresh";
123
- iss: string;
124
- aud: string | string[];
125
- iat: number;
126
- exp: number;
127
- nbf: number;
128
- jti: string;
88
+ export declare function isAccessToken(payload: VerifiedTokenPayload): payload is AccessTokenPayload;
89
+ export declare function isRefreshToken(payload: VerifiedTokenPayload): payload is RefreshTokenPayload;
90
+ export declare function isMachineToken(payload: VerifiedTokenPayload): payload is MachineTokenPayload;
91
+ export declare function isUserContext(ctx: AuthContext): ctx is UserContext;
92
+ export declare function isMachineContext(ctx: AuthContext): ctx is MachineContext;
93
+ /**
94
+ * Runtime assertion for service tokens.
95
+ */
96
+ export declare function assertMachineToken(payload: JWTPayload): asserts payload is MachineTokenPayload;
97
+ export interface ValidatedRequestData {
98
+ params?: Record<string, unknown>;
99
+ query?: Record<string, unknown>;
100
+ body?: Record<string, unknown>;
101
+ headers?: Record<string, unknown>;
129
102
  }
130
103
  declare module "express-serve-static-core" {
131
104
  interface Request {
132
105
  /**
133
- * Unique request ID — set by requestId middleware.
134
- * Respects upstream x-request-id forwarded by the gateway / load balancer.
135
- * Always present after requestId middleware runs.
106
+ * Request correlation ID.
136
107
  */
137
108
  id: string;
138
109
  /**
139
- * Validated + parsed request input (body / params / query / headers).
140
- * Typed as unknown — narrow at the route level using z.infer:
141
- *
142
- * const body = req.validated as z.infer<typeof CreateOrderSchema>;
143
- *
144
- * Never cast to any.
110
+ * Request metadata.
111
+ */
112
+ requestContext: RequestMetadataContext;
113
+ /**
114
+ * Validated request data.
145
115
  */
146
- validated?: unknown;
116
+ validated?: ValidatedRequestData;
147
117
  /**
148
- * Raw verified internal JWT payload.
149
- * Set by RequireAuthMiddleware after InternalJwtVerifier.verifyInternal().
150
- *
151
- * Prefer req.accessContext in route handlers and controllers.
152
- * Access internalAuth only when you need raw JWT claims (e.g. jti).
118
+ * Raw verified JWT payload.
153
119
  */
154
- internalAuth?: InternalJwtPayload;
120
+ authPayload?: VerifiedTokenPayload;
155
121
  /**
156
- * Clean access context — built from internalAuth + Redis permission cache.
157
- * This is what route handlers, controllers, and domain services consume.
158
- *
159
- * if (isHumanContext(req.accessContext)) {
160
- * const { accountId, accountRole, perms } = req.accessContext;
161
- * }
122
+ * Runtime authenticated identity.
162
123
  */
163
- accessContext?: AccessContext;
124
+ authContext?: AuthContext;
164
125
  }
165
126
  }
166
127
  export {};
@@ -1,60 +1,41 @@
1
1
  "use strict";
2
2
  /**
3
- * EXPRESS TYPE AUGMENTATION + JWT PAYLOAD TYPES (@discover-cloud/shared)
4
- * ──────────────────────────────────────────────────────────────────────────
5
- * Single source of truth for everything attached to Express's Request object
6
- * and all internal JWT payload shapes.
7
- *
8
- * Import in each service's entrypoint to activate the augmentation:
9
- * import "@discover-cloud/shared/types/express";
10
- *
11
- * ─── Permission flow ────────────────────────────────────────────────────
12
- * JWT carries: accountId + accountRole (lean payload, no perms)
13
- * Redis derives: GlobalPermission[] (role map on cache miss)
14
- * AccessContext has: perms (resolved by auth middleware)
15
- *
16
- * perms never touch the JWT — they live in the Redis permission cache and
17
- * are resolved into req.accessContext by RequireAuthMiddleware per request.
18
- * ────────────────────────────────────────────────────────────────────────
3
+ * @file express.types.ts
4
+ * @description Express Request type augmentations and authentication context definitions.
19
5
  */
20
6
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.isHumanPayload = isHumanPayload;
22
- exports.isMachinePayload = isMachinePayload;
23
- exports.assertMachinePayload = assertMachinePayload;
24
- exports.isHumanContext = isHumanContext;
7
+ exports.isAccessToken = isAccessToken;
8
+ exports.isRefreshToken = isRefreshToken;
9
+ exports.isMachineToken = isMachineToken;
10
+ exports.isUserContext = isUserContext;
25
11
  exports.isMachineContext = isMachineContext;
26
- /* ====================================================================
27
- 4. TYPE GUARDS
28
-
29
- Use these over raw property checks — TypeScript narrows the union
30
- correctly through predicate functions on both InternalJwtPayload
31
- and AccessContext.
32
- ==================================================================== */
33
- function isHumanPayload(payload) {
34
- return payload.isMachine === false;
12
+ exports.assertMachineToken = assertMachineToken;
13
+ /* ---------------------------------------------------------------------------
14
+ Type Guards
15
+ --------------------------------------------------------------------------- */
16
+ function isAccessToken(payload) {
17
+ return payload.typ === "access";
35
18
  }
36
- function isMachinePayload(payload) {
37
- return payload.isMachine === true;
19
+ function isRefreshToken(payload) {
20
+ return payload.typ === "refresh";
21
+ }
22
+ function isMachineToken(payload) {
23
+ return payload.typ === "machine";
24
+ }
25
+ function isUserContext(ctx) {
26
+ return ctx.type === "user";
27
+ }
28
+ function isMachineContext(ctx) {
29
+ return ctx.type === "machine";
38
30
  }
39
31
  /**
40
- * Runtime assertion for jose.jwtVerify() results.
41
- *
42
- * jose returns a generic JWTPayload because it cannot know
43
- * our custom claims. After verification + this assertion,
44
- * TypeScript safely treats it as MachineInternalJwtPayload.
32
+ * Runtime assertion for service tokens.
45
33
  */
46
- function assertMachinePayload(payload) {
47
- if (payload["typ"] !== "internal" ||
48
- payload["isMachine"] !== true ||
34
+ function assertMachineToken(payload) {
35
+ if (payload["typ"] !== "machine" ||
49
36
  typeof payload["serviceId"] !== "string" ||
50
37
  typeof payload["jti"] !== "string" ||
51
- typeof payload["caller"] !== "string") {
52
- throw new Error("Invalid machine JWT payload");
38
+ !Array.isArray(payload["scopes"])) {
39
+ throw new Error("Invalid machine token payload");
53
40
  }
54
41
  }
55
- function isHumanContext(ctx) {
56
- return ctx.kind === "human";
57
- }
58
- function isMachineContext(ctx) {
59
- return ctx.kind === "machine";
60
- }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @file date.util.ts
3
+ * @description Date formatting and serialization helper utilities.
4
+ */
5
+ export declare const toIso: (date: Date) => string;
6
+ export declare const toIsoOrNull: (date: Date | null | undefined) => string | null;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * @file date.util.ts
4
+ * @description Date formatting and serialization helper utilities.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.toIsoOrNull = exports.toIso = void 0;
8
+ const toIso = (date) => date.toISOString();
9
+ exports.toIso = toIso;
10
+ const toIsoOrNull = (date) => date ? date.toISOString() : null;
11
+ exports.toIsoOrNull = toIsoOrNull;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @file env.util.ts
3
+ * @description Helper functions to retrieve and validate environment variable settings safely.
4
+ */
5
+ export declare function getEnv(name: string): string;
6
+ export declare function getEnvOptional(name: string): string | undefined;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * @file env.util.ts
4
+ * @description Helper functions to retrieve and validate environment variable settings safely.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getEnv = getEnv;
8
+ exports.getEnvOptional = getEnvOptional;
9
+ function getEnv(name) {
10
+ const value = process.env[name];
11
+ if (!value) {
12
+ throw new Error(`Missing required environment variable: ${name}. Ensure it is set in your .env file.`);
13
+ }
14
+ return value;
15
+ }
16
+ function getEnvOptional(name) {
17
+ const value = process.env[name];
18
+ return value === "" ? undefined : value;
19
+ }