@heliyos/heliyos-api-core 1.0.70 → 1.0.72

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.
@@ -15,6 +15,7 @@ export interface IAuthResponseApiKey {
15
15
  policy: IAuthResponseApiKeyPolicy[] | undefined;
16
16
  userId: string;
17
17
  organizationId: string;
18
+ organizationBillingStatus?: string;
18
19
  }
19
20
  interface IAuthResponseApiKeyPolicy {
20
21
  resource: string;
@@ -23,6 +23,37 @@ exports.authentication = void 0;
23
23
  const basic_auth_1 = __importDefault(require("basic-auth"));
24
24
  const customError_1 = require("./@types/globals/customError");
25
25
  const _1 = require(".");
26
+ const BASIC_AUTH_ALLOWED_PATH_PREFIXES = [
27
+ "/v1/auth/",
28
+ "/v1/platform/billing/internal/",
29
+ "/v1/platform/usage/internal/log",
30
+ ];
31
+ const canAccessWithBasicAuth = (path) => {
32
+ if (!path) {
33
+ return false;
34
+ }
35
+ return BASIC_AUTH_ALLOWED_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
36
+ };
37
+ const getCandidateSessionCookieNames = () => {
38
+ const explicitNodeEnv = String(process.env.NODE_ENV || "").trim();
39
+ const explicitPublicEnv = String(process.env.NEXT_PUBLIC_ENV || "").trim();
40
+ const names = new Set(["user_session"]);
41
+ [explicitNodeEnv, explicitPublicEnv, "production", "development", "local"]
42
+ .filter(Boolean)
43
+ .forEach((envName) => names.add(`${envName}_user_session`));
44
+ return Array.from(names);
45
+ };
46
+ const getUserSessionCookieFromRequest = (req) => {
47
+ const cookies = req.cookies || {};
48
+ const cookieNames = getCandidateSessionCookieNames();
49
+ for (const name of cookieNames) {
50
+ const value = cookies[name];
51
+ if (value) {
52
+ return value;
53
+ }
54
+ }
55
+ return undefined;
56
+ };
26
57
  /**
27
58
  * Function to check internal and external authentication
28
59
  * @param req
@@ -36,7 +67,7 @@ const authentication = (req, res, next) => __awaiter(void 0, void 0, void 0, fun
36
67
  const container = {
37
68
  input: {
38
69
  authentication_header: req.headers.authorization,
39
- user_session_cookie: req.cookies.user_session,
70
+ user_session_cookie: getUserSessionCookieFromRequest(req),
40
71
  ip: getIp(req),
41
72
  auth_type: undefined,
42
73
  },
@@ -46,7 +77,10 @@ const authentication = (req, res, next) => __awaiter(void 0, void 0, void 0, fun
46
77
  },
47
78
  };
48
79
  // Check for the type of authentication
49
- checkAuthType(container, res);
80
+ const authTypeCheckResponse = checkAuthType(container, res);
81
+ if (authTypeCheckResponse) {
82
+ return;
83
+ }
50
84
  // Based on type, do further authentication
51
85
  // Either of BASIC / COOKIE / BEARER
52
86
  const authenticationResponse = yield authenticateRequest(container);
@@ -62,6 +96,16 @@ const authentication = (req, res, next) => __awaiter(void 0, void 0, void 0, fun
62
96
  message: "User not authenticated, Invalid token",
63
97
  });
64
98
  }
99
+ if (container.output.isBasicAuth &&
100
+ !canAccessWithBasicAuth(req.path || req.originalUrl || "")) {
101
+ return res.status(403).json({
102
+ error: {
103
+ status: 403,
104
+ err_msg: "FORBIDDEN",
105
+ },
106
+ message: "Basic authentication is restricted to internal service endpoints.",
107
+ });
108
+ }
65
109
  // Set logged in user data which can be used later on
66
110
  setLoggedInUser(container, req);
67
111
  // Move to next chain
@@ -137,7 +181,15 @@ const isJwtToken = (token) => {
137
181
  try {
138
182
  if (token.indexOf(".") > -1) {
139
183
  const token_parts = token.split(".");
140
- const token_detail_string = Buffer.from(token_parts[0], "base64");
184
+ if (!token_parts[0]) {
185
+ return false;
186
+ }
187
+ const normalizedHeader = token_parts[0]
188
+ .replace(/-/g, "+")
189
+ .replace(/_/g, "/");
190
+ const padding = (4 - (normalizedHeader.length % 4)) % 4;
191
+ const paddedHeader = normalizedHeader + "=".repeat(padding);
192
+ const token_detail_string = Buffer.from(paddedHeader, "base64");
141
193
  const token_detail = JSON.parse(token_detail_string.toString());
142
194
  if (token_detail.typ && token_detail.typ.toUpperCase() === "JWT") {
143
195
  return true;
@@ -233,7 +285,7 @@ const setLoggedInUser = function (container, req) {
233
285
  //
234
286
  // Handle Cookie and Bearer token
235
287
  const { output: { loggedInUser }, } = container;
236
- const { token, userId, organizationId, role, userFullName } = loggedInUser;
288
+ const { token, userId, organizationId, role, userFullName, organizationBillingStatus } = loggedInUser;
237
289
  // Modify req object with logged in user data
238
290
  req.loggedInUser = {
239
291
  token,
@@ -241,6 +293,7 @@ const setLoggedInUser = function (container, req) {
241
293
  organizationId,
242
294
  role,
243
295
  userFullName,
296
+ organizationBillingStatus,
244
297
  auth_type,
245
298
  };
246
299
  return undefined;
@@ -266,6 +319,7 @@ const callAuthApiServer = (token) => __awaiter(void 0, void 0, void 0, function*
266
319
  organizationId: authResult.data.data.payload.organizationId,
267
320
  role: authResult.data.data.payload.role,
268
321
  userFullName: authResult.data.data.payload.userFullName,
322
+ organizationBillingStatus: authResult.data.data.payload.organizationBillingStatus,
269
323
  };
270
324
  }
271
325
  else {
@@ -273,7 +327,9 @@ const callAuthApiServer = (token) => __awaiter(void 0, void 0, void 0, function*
273
327
  }
274
328
  }
275
329
  catch (error) {
276
- console.log(error);
330
+ _1.logger.error("Error while verifying bearer token with auth service", {
331
+ error: (error === null || error === void 0 ? void 0 : error.message) || error,
332
+ });
277
333
  throw error;
278
334
  }
279
335
  });
@@ -286,7 +342,7 @@ const verifyApiKey = (apiKey) => __awaiter(void 0, void 0, void 0, function* ()
286
342
  var _a, _b, _c, _d;
287
343
  try {
288
344
  // Call api key service to verify api key
289
- const apiRes = yield _1.axios.authServer.post(`/v2/auth/api_key/verify`, {
345
+ const apiRes = yield _1.axios.authServer.post(`/v1/auth/api_key/verify`, {
290
346
  apiKey,
291
347
  });
292
348
  if ((_b = (_a = apiRes.data) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.isValid) {
@@ -298,7 +354,9 @@ const verifyApiKey = (apiKey) => __awaiter(void 0, void 0, void 0, function* ()
298
354
  }
299
355
  }
300
356
  catch (err) {
301
- console.log(err);
357
+ _1.logger.error("Error while verifying API key with auth service", {
358
+ error: (err === null || err === void 0 ? void 0 : err.message) || err,
359
+ });
302
360
  // If verified api key not found then throw error
303
361
  const error = new customError_1.HttpError("Invalid api key.");
304
362
  error.status = "403";
@@ -1,11 +1,21 @@
1
1
  /**
2
- * Authorize the user with the resource action
2
+ * Authorize the user with the resource action.
3
+ * Also enforces billing gate: write actions (ADD/EDIT/DELETE/CREATE)
4
+ * are blocked when the org's billingStatus is canceled or expired.
5
+ * Read actions (VIEW_*) always pass through.
6
+ *
3
7
  * @param organizationId
4
8
  * @param userId
5
9
  * @param resourceAction
10
+ * @param options.allowInactive - bypass inactive org check in auth service
11
+ * @param options.billingStatus - org billing status from session; used for write-gate
6
12
  * @returns
7
13
  */
8
- export declare const authorizeUser: <T = string, U = string>(organizationId: T, userId: U, resourceAction: string) => Promise<{
14
+ export declare const authorizeUser: <T = string, U = string>(organizationId: T, userId: U, resourceAction: string, options?: {
15
+ allowInactive?: boolean;
16
+ billingStatus?: string;
17
+ userRole?: string;
18
+ }) => Promise<{
9
19
  isAllowed: string;
10
20
  userRole: string;
11
21
  }>;
@@ -12,25 +12,51 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.authorizeUser = void 0;
13
13
  const customError_1 = require("./@types/globals/customError");
14
14
  const axios_1 = require("./axios");
15
+ const BILLING_BLOCKED_STATUSES = ["canceled", "incomplete_expired"];
15
16
  /**
16
- * Authorize the user with the resource action
17
+ * Authorize the user with the resource action.
18
+ * Also enforces billing gate: write actions (ADD/EDIT/DELETE/CREATE)
19
+ * are blocked when the org's billingStatus is canceled or expired.
20
+ * Read actions (VIEW_*) always pass through.
21
+ *
17
22
  * @param organizationId
18
23
  * @param userId
19
24
  * @param resourceAction
25
+ * @param options.allowInactive - bypass inactive org check in auth service
26
+ * @param options.billingStatus - org billing status from session; used for write-gate
20
27
  * @returns
21
28
  */
22
29
  // eslint-disable-next-line import/prefer-default-export, @typescript-eslint/naming-convention
23
- const authorizeUser = (organizationId, userId, resourceAction) => __awaiter(void 0, void 0, void 0, function* () {
30
+ const authorizeUser = (organizationId, userId, resourceAction, options) => __awaiter(void 0, void 0, void 0, function* () {
24
31
  try {
25
- const authenticationResponse = yield axios_1.coreAxios.authServer.post(`v1/auth/user/${userId}`, { resourceAction, organizationId });
32
+ // Platform admin roles bypass billing gate entirely
33
+ const PLATFORM_ROLES = ["SUPER_ADMIN", "STAFF", "SUPPORT"];
34
+ const isPlatformAdmin = PLATFORM_ROLES.includes(String((options === null || options === void 0 ? void 0 : options.userRole) || "").toUpperCase());
35
+ // Billing gate: block write actions for orgs with canceled/expired billing
36
+ // Platform admins (SUPER_ADMIN etc.) are exempt
37
+ const isWriteAction = !resourceAction.startsWith("VIEW_");
38
+ if (!isPlatformAdmin &&
39
+ isWriteAction &&
40
+ (options === null || options === void 0 ? void 0 : options.billingStatus) &&
41
+ BILLING_BLOCKED_STATUSES.includes(options.billingStatus)) {
42
+ const error = new customError_1.HttpError("Your subscription is inactive. Please renew to continue.");
43
+ error.status = "403";
44
+ throw error;
45
+ }
46
+ const authenticationResponse = yield axios_1.coreAxios.authServer.post(`/v1/auth/user/${userId}`, {
47
+ resourceAction,
48
+ organizationId,
49
+ allowInactive: Boolean(options === null || options === void 0 ? void 0 : options.allowInactive),
50
+ });
26
51
  return authenticationResponse.data.data;
27
52
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
28
53
  }
29
54
  catch (err) {
30
- if ((err === null || err === void 0 ? void 0 : err.status) === 401) {
55
+ const status = String((err === null || err === void 0 ? void 0 : err.status) || "");
56
+ if (status === "401") {
31
57
  throw err;
32
58
  }
33
- if ((err === null || err === void 0 ? void 0 : err.status) === 501) {
59
+ if (status === "403") {
34
60
  throw err;
35
61
  }
36
62
  const error = new customError_1.HttpError("Something went wrong with Authentication");
@@ -19,6 +19,7 @@ const serve_static_1 = __importDefault(require("serve-static"));
19
19
  const authentication_1 = require("./authentication");
20
20
  const allowedOrigin_1 = require("./allowedOrigin");
21
21
  const customError_1 = require("./@types/globals/customError");
22
+ const logger_1 = require("./logger");
22
23
  const genericErrorMessage = "Something went wrong";
23
24
  const defaultErrorStatusCode = 500;
24
25
  const nonProductionEnvironments = new Set(["development", "local", "test"]);
@@ -189,19 +190,12 @@ const handle_errors = (error, _, res, __) => {
189
190
  description: sanitizeErrorDescription(description),
190
191
  };
191
192
  // Log original error
192
- console.error("=== Begin Error ===\n---\n" +
193
- "Error: " +
194
- message +
195
- "\n" +
196
- "Status: " +
197
- status +
198
- "\n" +
199
- "Desc: " +
200
- description +
201
- "\n" +
202
- "Stack: " +
203
- stack +
204
- "\n---\n=== End Error ===");
193
+ logger_1.logger.error("Unhandled middleware error", {
194
+ message,
195
+ status,
196
+ description,
197
+ stack,
198
+ });
205
199
  // Provide stack track in env development and local
206
200
  if (!isProductionEnvironment()) {
207
201
  response.error = Object.assign({ stack }, error);
@@ -4,6 +4,8 @@ interface IAuthPolicy {
4
4
  ROLES_PERMISSIONS: RolesPermissionsType;
5
5
  }
6
6
  export type RolesPermissionsType = {
7
+ MEMBER?: string[];
8
+ AGENCY?: string[];
7
9
  TEAM_MEMBER: string[];
8
10
  ADMIN: string[];
9
11
  OWNER: string[];
@@ -700,3 +700,9 @@ exports.authPolicy = {
700
700
  ],
701
701
  },
702
702
  };
703
+ // MEMBER is the canonical name (renamed from TEAM_MEMBER)
704
+ const memberRolePermissions = Array.from(new Set(exports.authPolicy.ROLES_PERMISSIONS.TEAM_MEMBER));
705
+ exports.authPolicy.ROLES_PERMISSIONS.MEMBER = memberRolePermissions;
706
+ // AGENCY has the same permissions as OWNER
707
+ const agencyRolePermissions = Array.from(new Set(exports.authPolicy.ROLES_PERMISSIONS.OWNER));
708
+ exports.authPolicy.ROLES_PERMISSIONS.AGENCY = agencyRolePermissions;
@@ -773,12 +773,26 @@ export const authPolicy: IAuthPolicy = {
773
773
  },
774
774
  };
775
775
 
776
+ // MEMBER is the canonical name (renamed from TEAM_MEMBER)
777
+ const memberRolePermissions = Array.from(
778
+ new Set(authPolicy.ROLES_PERMISSIONS.TEAM_MEMBER)
779
+ );
780
+ authPolicy.ROLES_PERMISSIONS.MEMBER = memberRolePermissions;
781
+
782
+ // AGENCY has the same permissions as OWNER
783
+ const agencyRolePermissions = Array.from(
784
+ new Set(authPolicy.ROLES_PERMISSIONS.OWNER)
785
+ );
786
+ authPolicy.ROLES_PERMISSIONS.AGENCY = agencyRolePermissions;
787
+
776
788
  interface IAuthPolicy {
777
789
  RESOURCES_ACTIONS: ResourcePolicyActionsType;
778
790
  ROLES_PERMISSIONS: RolesPermissionsType;
779
791
  }
780
792
 
781
793
  export type RolesPermissionsType = {
794
+ MEMBER?: string[];
795
+ AGENCY?: string[];
782
796
  TEAM_MEMBER: string[];
783
797
  ADMIN: string[];
784
798
  OWNER: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heliyos/heliyos-api-core",
3
- "version": "1.0.70",
3
+ "version": "1.0.72",
4
4
  "description": "Heliyos's core api functions and middlewares. Its a private package hosted on npm.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {