From a49d035e807c515e55a18915cbb5b32b6c64823e Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 4 Feb 2024 15:50:46 +0100 Subject: [PATCH] recaptcha --- env.example | 4 +- src/controllers/userController.ts | 39 +++++++++++-- src/utils/recaptcha.ts | 56 +++++++++++++++++++ .../6d86ae13-05cb-4d3d-a9c2-112783d133b9.json | 1 - 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/utils/recaptcha.ts delete mode 100644 user-profile-exports/6d86ae13-05cb-4d3d-a9c2-112783d133b9.json diff --git a/env.example b/env.example index 346b749..b302c18 100644 --- a/env.example +++ b/env.example @@ -28,4 +28,6 @@ RABBITMQ_USERNAME= RABBITMQ_PASSWORD= RABBITMQ_MAIL_QUEUE= -ACCOUNT_EXPORT_URL= \ No newline at end of file +ACCOUNT_EXPORT_URL= + +RECAPTCHA_SECRET_KEY= \ No newline at end of file diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts index d59cc64..f40d82b 100644 --- a/src/controllers/userController.ts +++ b/src/controllers/userController.ts @@ -33,11 +33,19 @@ import Session from "../models/session"; import Feedback from "../models/feedback"; import fs from "fs-extra"; import { channel } from "../rabbitmq/rabbitmq"; +import verifyCaptcha from "../utils/recaptcha"; export async function SignUp(req: Request, res: Response) { try { - let { companyName, username, accountName, password, language, rememberMe } = - req.body; + let { + companyName, + username, + accountName, + password, + language, + rememberMe, + recaptcha, + } = req.body; // validate request @@ -51,11 +59,23 @@ export async function SignUp(req: Request, res: Response) { !isUsernameValid(username) || !isAccountNameValid(accountName) || !isLanguageCodeValid(language) || - rememberMe === undefined + rememberMe === undefined || + !recaptcha ) { return res.status(400).send({ err: "invalid request" }); } + // validate recaptcha + + const recaptchaValid = await verifyCaptcha( + recaptcha, + req.headers["x-real-ip"] as string + ); + + if (!recaptchaValid) { + return res.status(400).send({ err: "invalid request" }); + } + accountName = accountName.toLowerCase(); // check if user already exists @@ -129,7 +149,7 @@ export async function SignUp(req: Request, res: Response) { export async function Login(req: Request, res: Response) { try { - let { accountName, password, rememberMe } = req.body; + let { accountName, password, rememberMe, recaptcha } = req.body; // validate request @@ -163,6 +183,17 @@ export async function Login(req: Request, res: Response) { return res.status(200).send({ state: user.state }); } + // validate recaptcha + + const recaptchaValid = await verifyCaptcha( + recaptcha, + req.headers["x-real-ip"] as string + ); + + if (!recaptchaValid) { + return res.status(400).send({ err: "invalid request" }); + } + // decode password const decodedPassword = decodeBase64(password); diff --git a/src/utils/recaptcha.ts b/src/utils/recaptcha.ts new file mode 100644 index 0000000..3446b36 --- /dev/null +++ b/src/utils/recaptcha.ts @@ -0,0 +1,56 @@ +import logger from "../logger/logger"; + +// crash if no recaptcha secret key are set +if (!process.env.RECAPTCHA_SECRET_KEY) { + throw new Error("no recaptcha secret key found!"); +} + +const encodedSecretKey = encodeURIComponent(process.env.RECAPTCHA_SECRET_KEY); + +async function verifyCaptcha(token: string, ip?: string): Promise { + return new Promise(async (resolve, reject) => { + if (typeof token !== "string" || token.trim() === "") { + reject("Invalid token"); + return; + } + + if (!encodedSecretKey) { + reject("captcha secret not set"); + return; + } + + let url; + + if (!ip) { + url = + "https://www.google.com/recaptcha/api/siteverify?secret=" + + encodedSecretKey + + "&response=" + + encodeURIComponent(token); + } else { + url = + "https://www.google.com/recaptcha/api/siteverify?secret=" + + encodedSecretKey + + "&response=" + + encodeURIComponent(token) + + "&remoteip=" + + ip; + } + + try { + const res = await fetch(url, { method: "POST" }); + const json = await res.json(); + + if (json.success) { + resolve(true); + } else { + reject("captcha failed"); + } + } catch (error) { + reject("Error verifying captcha"); + logger.error("Error verifying captcha:", error); + } + }); +} + +export default verifyCaptcha; diff --git a/user-profile-exports/6d86ae13-05cb-4d3d-a9c2-112783d133b9.json b/user-profile-exports/6d86ae13-05cb-4d3d-a9c2-112783d133b9.json deleted file mode 100644 index 46273c0..0000000 --- a/user-profile-exports/6d86ae13-05cb-4d3d-a9c2-112783d133b9.json +++ /dev/null @@ -1 +0,0 @@ -{"user":{"user_id":"6d86ae13-05cb-4d3d-a9c2-112783d133b9","username":"Mikael cool","account_name":"mikael","calendar_max_future_booking_days":null,"calendar_min_earliest_booking_time":null,"calendar_using_primary_calendar":true,"language":"de","analytics_enabled":true}}