diff --git a/packages/api/src/routers/auth/auth_router.ts b/packages/api/src/routers/auth/auth_router.ts index 01be9c019..da7443587 100644 --- a/packages/api/src/routers/auth/auth_router.ts +++ b/packages/api/src/routers/auth/auth_router.ts @@ -47,6 +47,7 @@ import { } from './google_auth' import { createWebAuthToken } from './jwt_helpers' import { createMobileAccountCreationResponse } from './mobile/account_creation' +import { verifyChallengeRecaptcha } from '../../utils/recaptcha' export interface SignupRequest { email: string @@ -55,6 +56,7 @@ export interface SignupRequest { username: string bio?: string pictureUrl?: string + recaptchaToken?: string } const signToken = promisify(jwt.sign) @@ -499,7 +501,24 @@ export function authRouter() { `${env.client.url}/auth/email-signup?errorCodes=INVALID_CREDENTIALS` ) } - const { email, password, name, username, bio, pictureUrl } = req.body + const { + email, + password, + name, + username, + bio, + pictureUrl, + recaptchaToken, + } = req.body + + if (recaptchaToken) { + const verified = await verifyChallengeRecaptcha(recaptchaToken) + if (!verified) { + return res.redirect( + `${env.client.url}/auth/email-signup?errorCodes=UNKNOWN` + ) + } + } function isURLPresent(input: string): boolean { const urlRegex = /(https?:\/\/[^\s]+)/g diff --git a/packages/api/src/utils/recaptcha.ts b/packages/api/src/utils/recaptcha.ts new file mode 100644 index 000000000..2fa69a626 --- /dev/null +++ b/packages/api/src/utils/recaptcha.ts @@ -0,0 +1,49 @@ +import axios from 'axios' + +type RecaptchaResponse = { + success: Boolean + hostname: string + score?: number + action?: string +} + +const isRecaptchaResponse = (data: any): data is RecaptchaResponse => { + return ( + 'success' in data && + 'hostname' in data && + 'score' in data && + 'action' in data + ) +} + +export const verifyChallengeRecaptcha = async ( + token: string +): Promise => { + if (!process.env.RECAPTCHA_CHALLENGE_SECRET_KEY) { + return false + } + + const url = `https://www.google.com/recaptcha/api/siteverify` + const params = new URLSearchParams({ + secret: process.env.RECAPTCHA_CHALLENGE_SECRET_KEY, + response: token, + }) + + try { + const response = await axios.post(url, params) + console.log('recaptcha response: ', response) + + if (!response.data || !response.data.success) { + throw new Error('Failed to verify reCAPTCHA') + } + + const json = response.data + if (!isRecaptchaResponse(json)) { + return false + } + return json.success + } catch (error) { + console.error('Error verifying reCAPTCHA:', error) + return false + } +}