From 8642a54fec5e976258f4f95a4dac4b4c8d5ba7de Mon Sep 17 00:00:00 2001 From: Nawaz Dhandala Date: Mon, 24 Nov 2025 12:08:09 +0000 Subject: [PATCH] feat: add captcha configuration and verification support --- App/FeatureSet/Identity/API/Authentication.ts | 11 +++++++++++ Common/Server/EnvironmentConfig.ts | 11 +++++++++++ Common/UI/Config.ts | 3 +++ config.example.env | 5 +++++ 4 files changed, 30 insertions(+) diff --git a/App/FeatureSet/Identity/API/Authentication.ts b/App/FeatureSet/Identity/API/Authentication.ts index 0c8a991010..2b9802e4f2 100644 --- a/App/FeatureSet/Identity/API/Authentication.ts +++ b/App/FeatureSet/Identity/API/Authentication.ts @@ -38,6 +38,7 @@ import Express, { getClientIp, headerValueToString, } from "Common/Server/Utils/Express"; +import CaptchaUtil from "Common/Server/Utils/Captcha"; import logger from "Common/Server/Utils/Logger"; import Response from "Common/Server/Utils/Response"; import TotpAuth from "Common/Server/Utils/TotpAuth"; @@ -107,6 +108,16 @@ router.post( ); } + const miscDataProps: JSONObject = + (req.body["miscDataProps"] as JSONObject) || {}; + + await CaptchaUtil.verifyCaptcha({ + token: + (miscDataProps["captchaToken"] as string | undefined) || + (req.body["captchaToken"] as string | undefined), + remoteIp: getClientIp(req) || null, + }); + const data: JSONObject = req.body["data"]; /* Creating a type that is a partial of the TBaseModel type. */ diff --git a/Common/Server/EnvironmentConfig.ts b/Common/Server/EnvironmentConfig.ts index 5a1eb484f3..e5bd8750d2 100644 --- a/Common/Server/EnvironmentConfig.ts +++ b/Common/Server/EnvironmentConfig.ts @@ -44,6 +44,8 @@ const FRONTEND_ENV_ALLOW_LIST: Array = [ "DISABLE_TELEMETRY", "SLACK_APP_CLIENT_ID", "MICROSOFT_TEAMS_APP_CLIENT_ID", + "CAPTCHA_ENABLED", + "CAPTCHA_SITE_KEY", ]; const FRONTEND_ENV_ALLOW_PREFIXES: Array = [ @@ -324,6 +326,15 @@ export const Host: string = process.env["HOST"] || ""; export const ProvisionSsl: boolean = process.env["PROVISION_SSL"] === "true"; +export const CaptchaEnabled: boolean = + process.env["CAPTCHA_ENABLED"] === "true"; + +export const CaptchaSecretKey: string = + process.env["CAPTCHA_SECRET_KEY"] || ""; + +export const CaptchaSiteKey: string = + process.env["CAPTCHA_SITE_KEY"] || ""; + export const WorkflowScriptTimeoutInMS: number = process.env[ "WORKFLOW_SCRIPT_TIMEOUT_IN_MS" ] diff --git a/Common/UI/Config.ts b/Common/UI/Config.ts index 541bd14081..d5242ffdd4 100644 --- a/Common/UI/Config.ts +++ b/Common/UI/Config.ts @@ -51,6 +51,9 @@ export const IS_ENTERPRISE_EDITION: boolean = env("IS_ENTERPRISE_EDITION") === "true"; export const BILLING_PUBLIC_KEY: string = env("BILLING_PUBLIC_KEY") || ""; +export const CAPTCHA_ENABLED: boolean = env("CAPTCHA_ENABLED") === "true"; +export const CAPTCHA_SITE_KEY: string = env("CAPTCHA_SITE_KEY") || ""; + // VAPID Configuration for Push Notifications export const VAPID_PUBLIC_KEY: string = env("VAPID_PUBLIC_KEY") || ""; diff --git a/config.example.env b/config.example.env index 01b6a84bc5..f59ea95263 100644 --- a/config.example.env +++ b/config.example.env @@ -15,6 +15,11 @@ ONEUPTIME_HTTP_PORT=80 # If you prefer to terminate TLS on an external reverse proxy, leave PROVISION_SSL=false and manage certificates yourself. HTTP_PROTOCOL=http +# Captcha configuration +CAPTCHA_ENABLED=false +CAPTCHA_SITE_KEY= +CAPTCHA_SECRET_KEY= + # Secrets - PLEASE CHANGE THESE. Please change these to something random. All of these can be different values. ONEUPTIME_SECRET=please-change-this-to-random-value DATABASE_PASSWORD=please-change-this-to-random-value