From 2b1d7c07a994d6373f2054c772977d88342e6d0a Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Thu, 9 Oct 2025 20:01:30 +0530 Subject: [PATCH 01/13] feat(auth): allow admin role to override tenant and org --- src/middlewares/authenticator.js | 55 +++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index 5ce40ccc..ab4a494d 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -10,7 +10,7 @@ const jwt = require('jsonwebtoken') const httpStatusCode = require('@generics/http-status') const common = require('@constants/common') const userQueries = require('@database/queries/users') -const roleQueries = require('@database/queries/user-role') + const rolePermissionMappingQueries = require('@database/queries/role-permission-mapping') const { Op } = require('sequelize') const responses = require('@helpers/responses') @@ -159,6 +159,7 @@ module.exports = async function (req, res, next) { token = extractedToken.trim() } else token = authHeader.trim() + let decodedToken let org try { decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET) @@ -210,10 +211,56 @@ module.exports = async function (req, res, next) { let isAdmin = false if (decodedToken.data.roles) { isAdmin = decodedToken.data.roles.some((role) => role.title == common.ADMIN_ROLE) - if (isAdmin) { - req.decodedToken = decodedToken.data - return next() + } + + if (isAdmin) { + // For admin users, allow overriding tenant_code, organization_id, and organization_code via headers + // Header names are configurable via environment variables with sensible defaults + const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME + const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME + const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME + + // Extract and sanitize header values (trim whitespace, case-insensitive header lookup) + const orgId = (req.headers[orgIdHeaderName.toLowerCase()] || '').trim() + const orgCode = (req.headers[orgCodeHeaderName.toLowerCase()] || '').trim() + const tenantCode = (req.headers[tenantCodeHeaderName.toLowerCase()] || '').trim() + + // If any override header is provided (non-empty after trim), all three must be present and non-empty + const hasAnyOverrideHeader = orgId || orgCode || tenantCode + if (hasAnyOverrideHeader) { + if (!orgId || !orgCode || !tenantCode) { + throw responses.failureResponse({ + message: { + key: 'ADD_ORG_HEADER', + interpolation: { + orgIdHeader: orgIdHeaderName, + orgCodeHeader: orgCodeHeaderName, + tenantCodeHeader: tenantCodeHeaderName, + }, + }, + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + + // Validate orgId is a valid positive integer + const parsedOrgId = parseInt(orgId, 10) + if (isNaN(parsedOrgId) || parsedOrgId <= 0) { + throw responses.failureResponse({ + message: 'INVALID_ORG_ID', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + + // Override the values from the token with sanitized header values + decodedToken.data.tenant_code = tenantCode + decodedToken.data.organization_id = orgId + decodedToken.data.organization_code = orgCode } + + req.decodedToken = decodedToken.data + //return next() } if (roleValidation) { From 738816541e2022a5aafef770de3c0af177b804b3 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Thu, 9 Oct 2025 20:01:50 +0530 Subject: [PATCH 02/13] feat(env): add optional headers for ORG_ID, ORG_CODE, and TENANT_CODE --- src/envVariables.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/envVariables.js b/src/envVariables.js index e640e1c0..8be773a9 100644 --- a/src/envVariables.js +++ b/src/envVariables.js @@ -458,6 +458,21 @@ let enviromentVariables = { value: 'true', }, }, + ORG_ID_HEADER_NAME: { + message: 'Required ORG_ID_HEADER_NAME', + optional: true, + default: 'X-Org-Id', + }, + ORG_CODE_HEADER_NAME: { + message: 'Required ORG_CODE_HEADER_NAME', + optional: true, + default: 'X-Org-Code', + }, + TENANT_CODE_HEADER_NAME: { + message: 'Required TENANT_CODE_HEADER_NAME', + optional: true, + default: 'X-Tenant-Code', + }, } let success = true From 490a558b76f72d9427fb9e965c8f630add1de098 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Thu, 9 Oct 2025 20:02:39 +0530 Subject: [PATCH 03/13] feat(locales): add organization header messages and validation for org ID --- src/locales/en.json | 4 +++- src/middlewares/authenticator.js | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 64d5f726..0aca7115 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -182,5 +182,7 @@ "REG_CODE_ERROR": "registration_code is not valid or unique.", "INVALID_REG_CODE_ERROR": "registration_codes {{errorMessage}}. Invalid Code(s) : {{errorValues}}", "UNIQUE_CONSTRAINT_ERROR": "{{fields}} is Invalid.", - "USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!" + "USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!", + "ADD_ORG_HEADER": "Please provide all required organization headers: {orgIdHeader}, {orgCodeHeader}, and {tenantCodeHeader} for admin override.", + "INVALID_ORG_ID": "Organization ID must be a valid positive integer." } diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index ab4a494d..cd0a9502 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -260,7 +260,6 @@ module.exports = async function (req, res, next) { } req.decodedToken = decodedToken.data - //return next() } if (roleValidation) { From c09c7187176d0c84400f1b5eda1f56430a1c75d1 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Thu, 9 Oct 2025 20:13:26 +0530 Subject: [PATCH 04/13] fix(locales): correct placeholder syntax in organization header message --- src/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en.json b/src/locales/en.json index 0aca7115..4929b788 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -183,6 +183,6 @@ "INVALID_REG_CODE_ERROR": "registration_codes {{errorMessage}}. Invalid Code(s) : {{errorValues}}", "UNIQUE_CONSTRAINT_ERROR": "{{fields}} is Invalid.", "USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!", - "ADD_ORG_HEADER": "Please provide all required organization headers: {orgIdHeader}, {orgCodeHeader}, and {tenantCodeHeader} for admin override.", + "ADD_ORG_HEADER": "Please provide all required organization headers: {{orgIdHeader}}, {{orgCodeHeader}}, and {{tenantCodeHeader}} for admin override.", "INVALID_ORG_ID": "Organization ID must be a valid positive integer." } From 65f352b359c3b19669cdda7b62367a6b1f06f1af Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Thu, 9 Oct 2025 20:20:16 +0530 Subject: [PATCH 05/13] feat(constants): update ORG_CODE_HEADER and TENANT_CODE_HEADER to use environment variables --- src/constants/common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants/common.js b/src/constants/common.js index 37791b56..64d83dbd 100644 --- a/src/constants/common.js +++ b/src/constants/common.js @@ -102,8 +102,8 @@ module.exports = { READ_ACCESS: 'r', TYPE_ALL: 'all', ENGLISH_LANGUGE_CODE: 'en', - ORG_CODE_HEADER: 'organizationcode', - TENANT_CODE_HEADER: 'tenantcode', + ORG_CODE_HEADER: process.env.ORG_CODE_HEADER_NAME, + TENANT_CODE_HEADER: process.env.TENANT_CODE_HEADER_NAME, DELETE_METHOD: 'DELETE', SEQUELIZE_FOREIGN_KEY_CONSTRAINT_ERROR: 'SequelizeForeignKeyConstraintError', BULK_INVITATION_VALIDITY: '604800000', //SET to one week by default if not set by tenant (In Sec), From e72da9e7c9edfe406b63c3dd91e6425492af0768 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Thu, 9 Oct 2025 20:23:21 +0530 Subject: [PATCH 06/13] fix(auth): replace organization_id with parsedOrgId in token decoding --- src/middlewares/authenticator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index cd0a9502..c64064e3 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -255,7 +255,7 @@ module.exports = async function (req, res, next) { // Override the values from the token with sanitized header values decodedToken.data.tenant_code = tenantCode - decodedToken.data.organization_id = orgId + decodedToken.data.organization_id = parsedOrgId decodedToken.data.organization_code = orgCode } From 4095b0f743dc0e003c156cb7e06dbfee6fb1ec42 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Fri, 10 Oct 2025 00:29:01 +0530 Subject: [PATCH 07/13] fix(auth): use strict equality for admin role check --- src/middlewares/authenticator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index c64064e3..8b119db0 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -210,7 +210,7 @@ module.exports = async function (req, res, next) { //check for admin user let isAdmin = false if (decodedToken.data.roles) { - isAdmin = decodedToken.data.roles.some((role) => role.title == common.ADMIN_ROLE) + isAdmin = decodedToken.data.roles.some((role) => role.title === common.ADMIN_ROLE) } if (isAdmin) { From 43dde738d058d5edef04ce0c51f3952ee0d86713 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Fri, 10 Oct 2025 00:30:39 +0530 Subject: [PATCH 08/13] fix(auth): remove redundant assignment of decodedToken to request object --- src/middlewares/authenticator.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index 8b119db0..ca9be5b7 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -258,8 +258,6 @@ module.exports = async function (req, res, next) { decodedToken.data.organization_id = parsedOrgId decodedToken.data.organization_code = orgCode } - - req.decodedToken = decodedToken.data } if (roleValidation) { From d909cf2e0507ff0394dfd58920ed4fad5865a63f Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Tue, 14 Oct 2025 01:42:32 +0530 Subject: [PATCH 09/13] refactor(headers): use env header names and lowercase defaults --- src/controllers/v1/admin.js | 4 ++-- src/controllers/v1/form.js | 2 +- src/controllers/v1/public.js | 2 +- src/controllers/v1/tenant.js | 4 ++-- src/envVariables.js | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/controllers/v1/admin.js b/src/controllers/v1/admin.js index b18b4c3f..45ca0183 100644 --- a/src/controllers/v1/admin.js +++ b/src/controllers/v1/admin.js @@ -110,7 +110,7 @@ module.exports = class Admin { req.body.organization_id, req.decodedToken.id, req.body?.identifier, - req.headers?.['tenant-id'], + req.headers?.[process.env.TENANT_CODE_HEADER_NAME], req.body?.phone_code ) return orgAdminCreation @@ -154,7 +154,7 @@ module.exports = class Admin { const result = await adminService.deactivateOrg( req.params.id, - req.headers?.['tenant-id'], + req.headers?.[process.env.TENANT_CODE_HEADER_NAME], req.decodedToken.id ) return result diff --git a/src/controllers/v1/form.js b/src/controllers/v1/form.js index c0c0ba0e..3bcf4b9f 100644 --- a/src/controllers/v1/form.js +++ b/src/controllers/v1/form.js @@ -100,7 +100,7 @@ module.exports = class Form { req.params.id, params, req?.decodedToken?.organization_id || null, - req?.decodedToken?.tenant_code || req?.headers?.tenant_code || null, + req?.decodedToken?.tenant_code || req?.headers?.[process.env.TENANT_CODE_HEADER_NAME] || null, domain ) return form diff --git a/src/controllers/v1/public.js b/src/controllers/v1/public.js index adf53139..64445a73 100644 --- a/src/controllers/v1/public.js +++ b/src/controllers/v1/public.js @@ -4,7 +4,7 @@ const { getDomainFromRequest } = require('@utils/domain') module.exports = class Public { async branding(req) { let domain = '' - let tenantCode = req?.headers?.tenantid || null + let tenantCode = req?.headers?.[process.env.TENANT_CODE_HEADER_NAME] || null if (!tenantCode) { domain = getDomainFromRequest(req) } diff --git a/src/controllers/v1/tenant.js b/src/controllers/v1/tenant.js index 8f9822bc..88e2666e 100644 --- a/src/controllers/v1/tenant.js +++ b/src/controllers/v1/tenant.js @@ -146,8 +146,8 @@ module.exports = class Tenant { const tenant = await tenantService.userBulkUpload( req.body.file_path, req.decodedToken.id, - req.headers.organization, - req.headers.tenant, + req.headers?.[process.env.ORG_CODE_HEADER_NAME], + req.headers?.[process.env.TENANT_CODE_HEADER_NAME], req?.body?.editable_fields, req?.body?.upload_type.toUpperCase() ) diff --git a/src/envVariables.js b/src/envVariables.js index 8be773a9..692b6dba 100644 --- a/src/envVariables.js +++ b/src/envVariables.js @@ -461,17 +461,17 @@ let enviromentVariables = { ORG_ID_HEADER_NAME: { message: 'Required ORG_ID_HEADER_NAME', optional: true, - default: 'X-Org-Id', + default: 'x-org-id', }, ORG_CODE_HEADER_NAME: { message: 'Required ORG_CODE_HEADER_NAME', optional: true, - default: 'X-Org-Code', + default: 'x-org-code', }, TENANT_CODE_HEADER_NAME: { message: 'Required TENANT_CODE_HEADER_NAME', optional: true, - default: 'X-Tenant-Code', + default: 'x-tenant-code', }, } let success = true From 1d8fbae155a9f82be2a367f8fc0e2a86f6a011ed Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Tue, 14 Oct 2025 02:22:19 +0530 Subject: [PATCH 10/13] refactor(headers): use common constants for header names --- src/constants/common.js | 5 +++-- src/controllers/v1/admin.js | 4 ++-- src/controllers/v1/form.js | 3 ++- src/controllers/v1/public.js | 3 ++- src/controllers/v1/tenant.js | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/constants/common.js b/src/constants/common.js index 64d83dbd..68580fbe 100644 --- a/src/constants/common.js +++ b/src/constants/common.js @@ -102,8 +102,9 @@ module.exports = { READ_ACCESS: 'r', TYPE_ALL: 'all', ENGLISH_LANGUGE_CODE: 'en', - ORG_CODE_HEADER: process.env.ORG_CODE_HEADER_NAME, - TENANT_CODE_HEADER: process.env.TENANT_CODE_HEADER_NAME, + ORG_CODE_HEADER: process.env.ORG_CODE_HEADER_NAME.toLowerCase(), + ORG_ID_HEADER: process.env.ORG_ID_HEADER_NAME.toLowerCase(), + TENANT_CODE_HEADER: process.env.TENANT_CODE_HEADER_NAME.toLowerCase(), DELETE_METHOD: 'DELETE', SEQUELIZE_FOREIGN_KEY_CONSTRAINT_ERROR: 'SequelizeForeignKeyConstraintError', BULK_INVITATION_VALIDITY: '604800000', //SET to one week by default if not set by tenant (In Sec), diff --git a/src/controllers/v1/admin.js b/src/controllers/v1/admin.js index 45ca0183..6ca70e08 100644 --- a/src/controllers/v1/admin.js +++ b/src/controllers/v1/admin.js @@ -110,7 +110,7 @@ module.exports = class Admin { req.body.organization_id, req.decodedToken.id, req.body?.identifier, - req.headers?.[process.env.TENANT_CODE_HEADER_NAME], + req.headers?.[common.TENANT_CODE_HEADER], req.body?.phone_code ) return orgAdminCreation @@ -154,7 +154,7 @@ module.exports = class Admin { const result = await adminService.deactivateOrg( req.params.id, - req.headers?.[process.env.TENANT_CODE_HEADER_NAME], + req.headers?.[common.TENANT_CODE_HEADER], req.decodedToken.id ) return result diff --git a/src/controllers/v1/form.js b/src/controllers/v1/form.js index 3bcf4b9f..31becc9b 100644 --- a/src/controllers/v1/form.js +++ b/src/controllers/v1/form.js @@ -7,6 +7,7 @@ // Dependencies const formsService = require('@services/form') +const common = require('@constants/common') module.exports = class Form { /** @@ -100,7 +101,7 @@ module.exports = class Form { req.params.id, params, req?.decodedToken?.organization_id || null, - req?.decodedToken?.tenant_code || req?.headers?.[process.env.TENANT_CODE_HEADER_NAME] || null, + req?.decodedToken?.tenant_code || req?.headers?.[common.TENANT_CODE_HEADER] || null, domain ) return form diff --git a/src/controllers/v1/public.js b/src/controllers/v1/public.js index 64445a73..5df953a7 100644 --- a/src/controllers/v1/public.js +++ b/src/controllers/v1/public.js @@ -1,10 +1,11 @@ const publicService = require('@services/public') const { getDomainFromRequest } = require('@utils/domain') +const common = require('@constants/common') module.exports = class Public { async branding(req) { let domain = '' - let tenantCode = req?.headers?.[process.env.TENANT_CODE_HEADER_NAME] || null + let tenantCode = req?.headers?.[common.TENANT_CODE_HEADER] || null if (!tenantCode) { domain = getDomainFromRequest(req) } diff --git a/src/controllers/v1/tenant.js b/src/controllers/v1/tenant.js index 88e2666e..0e628ed7 100644 --- a/src/controllers/v1/tenant.js +++ b/src/controllers/v1/tenant.js @@ -146,8 +146,8 @@ module.exports = class Tenant { const tenant = await tenantService.userBulkUpload( req.body.file_path, req.decodedToken.id, - req.headers?.[process.env.ORG_CODE_HEADER_NAME], - req.headers?.[process.env.TENANT_CODE_HEADER_NAME], + req.headers?.[common.ORG_CODE_HEADER], + req.headers?.[common.TENANT_CODE_HEADER], req?.body?.editable_fields, req?.body?.upload_type.toUpperCase() ) From 78168ee6c6f5c67a1c3f6e0f061bd19f93ca80a2 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Tue, 14 Oct 2025 02:36:44 +0530 Subject: [PATCH 11/13] feat(auth): validate org and tenant code in authenticator --- src/locales/en.json | 3 ++- src/middlewares/authenticator.js | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/locales/en.json b/src/locales/en.json index 4929b788..9eeb4e97 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -184,5 +184,6 @@ "UNIQUE_CONSTRAINT_ERROR": "{{fields}} is Invalid.", "USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!", "ADD_ORG_HEADER": "Please provide all required organization headers: {{orgIdHeader}}, {{orgCodeHeader}}, and {{tenantCodeHeader}} for admin override.", - "INVALID_ORG_ID": "Organization ID must be a valid positive integer." + "INVALID_ORG_ID": "Organization ID must be a valid positive integer.", + "INVALID_ORG_OR_TENANT_CODE": "The provided organization or tenant code is invalid or does not match." } diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index ca9be5b7..44478a5d 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -18,6 +18,7 @@ const utilsHelper = require('@generics/utils') const { verifyCaptchaToken } = require('@utils/captcha') const { getDomainFromRequest } = require('@utils/domain') const tenantDomainQueries = require('@database/queries/tenantDomain') +const organizationQueries = require('@database/queries/organization') async function checkPermissions(roleTitle, requestPath, requestMethod) { const parts = requestPath.match(/[^/]+/g) @@ -252,7 +253,21 @@ module.exports = async function (req, res, next) { responseCode: 'CLIENT_ERROR', }) } + const org = await organizationQueries.findOne({ + id: parsedOrgId, + code: orgCode, + tenant_code: tenantCode, + status: common.ACTIVE_STATUS, + deleted_at: null, + }) + if (!org) { + throw responses.failureResponse({ + message: 'INVALID_ORG_OR_TENANT_CODE', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } // Override the values from the token with sanitized header values decodedToken.data.tenant_code = tenantCode decodedToken.data.organization_id = parsedOrgId From 4299978183492fc7ab538506b711b509720bc144 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Tue, 14 Oct 2025 16:56:55 +0530 Subject: [PATCH 12/13] refactor(auth): remove orgId header requirement for admin override --- src/locales/en.json | 2 +- src/middlewares/authenticator.js | 31 ++++++++++--------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 9eeb4e97..9bb0110b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -183,7 +183,7 @@ "INVALID_REG_CODE_ERROR": "registration_codes {{errorMessage}}. Invalid Code(s) : {{errorValues}}", "UNIQUE_CONSTRAINT_ERROR": "{{fields}} is Invalid.", "USER_PROFILE_FETCHED_SUCCESSFULLY": "User profile fetched successfully!", - "ADD_ORG_HEADER": "Please provide all required organization headers: {{orgIdHeader}}, {{orgCodeHeader}}, and {{tenantCodeHeader}} for admin override.", + "ADD_ORG_HEADER": "Please provide all required organization headers: {{orgCodeHeader}}, and {{tenantCodeHeader}} for admin override.", "INVALID_ORG_ID": "Organization ID must be a valid positive integer.", "INVALID_ORG_OR_TENANT_CODE": "The provided organization or tenant code is invalid or does not match." } diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index 44478a5d..5afd4dcb 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -215,26 +215,23 @@ module.exports = async function (req, res, next) { } if (isAdmin) { - // For admin users, allow overriding tenant_code, organization_id, and organization_code via headers + // For admin users, allow overriding tenant_code and organization_code via headers // Header names are configurable via environment variables with sensible defaults - const orgIdHeaderName = process.env.ORG_ID_HEADER_NAME - const orgCodeHeaderName = process.env.ORG_CODE_HEADER_NAME - const tenantCodeHeaderName = process.env.TENANT_CODE_HEADER_NAME + const orgCodeHeaderName = common.ORG_CODE_HEADER + const tenantCodeHeaderName = common.TENANT_CODE_HEADER // Extract and sanitize header values (trim whitespace, case-insensitive header lookup) - const orgId = (req.headers[orgIdHeaderName.toLowerCase()] || '').trim() const orgCode = (req.headers[orgCodeHeaderName.toLowerCase()] || '').trim() const tenantCode = (req.headers[tenantCodeHeaderName.toLowerCase()] || '').trim() - // If any override header is provided (non-empty after trim), all three must be present and non-empty - const hasAnyOverrideHeader = orgId || orgCode || tenantCode + // If any override header is provided (non-empty after trim), both must be present and non-empty + const hasAnyOverrideHeader = orgCode || tenantCode if (hasAnyOverrideHeader) { - if (!orgId || !orgCode || !tenantCode) { + if (!orgCode || !tenantCode) { throw responses.failureResponse({ message: { key: 'ADD_ORG_HEADER', interpolation: { - orgIdHeader: orgIdHeaderName, orgCodeHeader: orgCodeHeaderName, tenantCodeHeader: tenantCodeHeaderName, }, @@ -244,17 +241,8 @@ module.exports = async function (req, res, next) { }) } - // Validate orgId is a valid positive integer - const parsedOrgId = parseInt(orgId, 10) - if (isNaN(parsedOrgId) || parsedOrgId <= 0) { - throw responses.failureResponse({ - message: 'INVALID_ORG_ID', - statusCode: httpStatusCode.bad_request, - responseCode: 'CLIENT_ERROR', - }) - } + // Query the database to find the organization based on orgCode and tenantCode const org = await organizationQueries.findOne({ - id: parsedOrgId, code: orgCode, tenant_code: tenantCode, status: common.ACTIVE_STATUS, @@ -268,9 +256,10 @@ module.exports = async function (req, res, next) { responseCode: 'CLIENT_ERROR', }) } - // Override the values from the token with sanitized header values + + // Override the values from the token with sanitized header values and fetched orgId decodedToken.data.tenant_code = tenantCode - decodedToken.data.organization_id = parsedOrgId + decodedToken.data.organization_id = org.id // Use the ID from the database decodedToken.data.organization_code = orgCode } } From 40ac766984d21bd1d7c619e35c2199d6113ba4a8 Mon Sep 17 00:00:00 2001 From: Nevil Mathew Date: Wed, 15 Oct 2025 11:12:35 +0530 Subject: [PATCH 13/13] refactor(auth): simplify tenant filter construction logic --- src/middlewares/authenticator.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index 5afd4dcb..a6daab70 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -111,18 +111,13 @@ module.exports = async function (req, res, next) { }) } - const domain = getDomainFromRequest(req) || null - const tenant_code = - req?.headers?.tenantId || - req?.headers?.tenantid || - req?.headers?.tenant_Id || - req?.headers?.tenant_id || - req?.headers?.tenant || - req?.headers?.tenant_code || - req.headers.tenantCode || - null - - const tenantFilter = domain ? { domain } : tenant_code ? { tenant_code } : null || {} + const tenantFilter = {} + const domain = getDomainFromRequest(req) + + const tenant_code = req?.headers?.[common.TENANT_CODE_HEADER] ?? null + + if (domain) tenantFilter.domain = domain + else if (tenant_code) tenantFilter.tenant_code = tenant_code if (Object.keys(tenantFilter).length > 0) { const tenantDomain = await tenantDomainQueries.findOne(tenantFilter, {