diff --git a/src/constants/common.js b/src/constants/common.js index ce9011075..74c54835d 100644 --- a/src/constants/common.js +++ b/src/constants/common.js @@ -89,4 +89,5 @@ module.exports = { DELETED_STATUS: 'DELETED', DEFAULT_ORG_VISIBILITY: 'PUBLIC', ROLE_TYPE_NON_SYSTEM: 0, + captchaEnabledAPIs: ['/user/v1/account/login'], } diff --git a/src/envVariables.js b/src/envVariables.js index 22d106dba..d8650137e 100644 --- a/src/envVariables.js +++ b/src/envVariables.js @@ -256,6 +256,29 @@ let enviromentVariables = { optional: true, default: 3600000, }, + CAPTCHA_ENABLE: { + message: 'Required CAPTCHA ENABLE true or false', + optional: false, + }, + CAPTCHA_SERVICE: { + message: 'Required CAPTCHA SERVICE', + optional: true, + default: 'googleRecaptcha', + }, + RECAPTCHA_SECRET_KEY: { + message: 'Required CAPTCHA SERVICE secret key', + optional: false, + }, + GOOGLE_RECAPTCHA_HOST: { + message: 'Required CAPTCHA Host IP', + optional: true, + default: 'https://www.google.com', + }, + GOOGLE_RECAPTCHA_URL: { + message: 'Required CAPTCHA SERVICE API URL', + optional: true, + default: '/recaptcha/api/siteverify', + }, SIGNED_URL_EXPIRY_IN_MILLISECONDS: { message: 'Required signed url expiration time in milliseconds', optional: true, diff --git a/src/middlewares/authenticator.js b/src/middlewares/authenticator.js index caed59bad..5f303c345 100644 --- a/src/middlewares/authenticator.js +++ b/src/middlewares/authenticator.js @@ -14,6 +14,7 @@ const roleQueries = require('@database/queries/user-role') const rolePermissionMappingQueries = require('@database/queries/role-permission-mapping') const { Op } = require('sequelize') const responses = require('@helpers/responses') +const { verifyCaptchaToken } = require('@utils/captcha') async function checkPermissions(roleTitle, requestPath, requestMethod) { const parts = requestPath.match(/[^/]+/g) @@ -58,6 +59,26 @@ module.exports = async function (req, res, next) { return false }) + // check if captcha check is enabled in the env + const isCaptchaEnabled = process.env.CAPTCHA_ENABLE.toLowerCase() == 'true' + + if (isCaptchaEnabled) { + // check if captcha is enabled for the route + const isCaptchaEnabledForRoute = common.captchaEnabledAPIs.includes(req.path) + if (isCaptchaEnabledForRoute) { + // get the token from API + const captchaToken = req.get('captcha-token') + // verify token + if (!(await verifyCaptchaToken(captchaToken))) { + throw responses.failureResponse({ + message: 'CAPTCHA_VERIFICATION_FAILED', + statusCode: httpStatusCode.unauthorized, + responseCode: 'UNAUTHORIZED', + }) + } + } + } + common.roleValidationPaths.map(function (path) { if (req.path.includes(path)) { roleValidation = true diff --git a/src/utils/captcha.js b/src/utils/captcha.js new file mode 100644 index 000000000..ac3783202 --- /dev/null +++ b/src/utils/captcha.js @@ -0,0 +1,25 @@ +'use strict' +const common = require('@constants/common') +const requester = require('@utils/requester') + +exports.verifyCaptchaToken = async (token, options = {}) => { + const headers = { + secret: process.env.RECAPTCHA_SECRET_KEY, + response: token, + } + const requestBody = {} + const queryParams = {} + let response + if (process.env.CAPTCHA_SERVICE == 'googleRecaptcha') { + response = await requester.post( + process.env.GOOGLE_RECAPTCHA_HOST, + process.env.GOOGLE_RECAPTCHA_URL, + headers, + requestBody, + queryParams + ) + } + if (response.success) return true + + return false +}