diff --git a/package-lock.json b/package-lock.json
index 6447d089..aa940f44 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
+ "react-google-recaptcha-v3": "^1.11.0",
"react-helmet": "6.1.0",
"react-loading-skeleton": "3.5.0",
"react-redux": "7.2.9",
@@ -22541,6 +22542,18 @@
}
}
},
+ "node_modules/react-google-recaptcha-v3": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/react-google-recaptcha-v3/-/react-google-recaptcha-v3-1.11.0.tgz",
+ "integrity": "sha512-kLQqpz/77m8+trpBwzqcxNtvWZYoZ/YO6Vm2cVTHW8hs80BWUfDpC7RDwuAvpswwtSYApWfaSpIDFWAIBNIYxQ==",
+ "dependencies": {
+ "hoist-non-react-statics": "^3.3.2"
+ },
+ "peerDependencies": {
+ "react": "^16.3 || ^17.0 || ^18.0 || ^19.0",
+ "react-dom": "^17.0 || ^18.0 || ^19.0"
+ }
+ },
"node_modules/react-helmet": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz",
diff --git a/package.json b/package.json
index 42406448..698a60fb 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"react-error-boundary": "^4.0.13",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-google-recaptcha-v3": "^1.11.0",
"react-helmet": "6.1.0",
"react-loading-skeleton": "3.5.0",
"react-redux": "7.2.9",
diff --git a/src/MainApp.jsx b/src/MainApp.jsx
index 7beaa3d1..08016e52 100755
--- a/src/MainApp.jsx
+++ b/src/MainApp.jsx
@@ -2,6 +2,7 @@ import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { AppProvider } from '@edx/frontend-platform/react';
+import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
import { Helmet } from 'react-helmet';
import { Navigate, Route, Routes } from 'react-router-dom';
@@ -34,31 +35,35 @@ registerIcons();
const MainApp = () => (
-
-
-
-
- } />
- }
- />
-
- }
- />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
+
+
+
+
+ {getConfig().ZENDESK_KEY && }
+
+ } />
+ }
+ />
+
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
);
diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx
index 50236e80..b1992183 100644
--- a/src/register/RegistrationPage.jsx
+++ b/src/register/RegistrationPage.jsx
@@ -8,6 +8,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { Form, Spinner, StatefulButton } from '@openedx/paragon';
import classNames from 'classnames';
import PropTypes from 'prop-types';
+import { useGoogleReCaptcha } from 'react-google-recaptcha-v3';
import { Helmet } from 'react-helmet';
import Skeleton from 'react-loading-skeleton';
@@ -60,6 +61,7 @@ import { trackRegistrationPageViewed, trackRegistrationSuccess } from '../tracki
const RegistrationPage = (props) => {
const { formatMessage } = useIntl();
const dispatch = useDispatch();
+ const { executeRecaptcha } = useGoogleReCaptcha();
const registrationEmbedded = isHostAvailableInQueryParams();
const platformName = getConfig().SITE_NAME;
@@ -106,6 +108,8 @@ const RegistrationPage = (props) => {
const [formStartTime, setFormStartTime] = useState(null);
// temporary error state for embedded experience because we don't want to show errors on blur
const [temporaryErrors, setTemporaryErrors] = useState({ ...backedUpFormData.errors });
+ const [captchaError, setCaptchaError] = useState('');
+ const intl = useIntl();
const { cta, host } = queryParams;
const buttonLabel = cta
@@ -230,7 +234,8 @@ const RegistrationPage = (props) => {
}
};
- const registerUser = () => {
+ const registerUser = async () => {
+ let recaptchaToken = '';
const totalRegistrationTime = (Date.now() - formStartTime) / 1000;
let payload = { ...formFields, app_name: APP_NAME };
@@ -259,16 +264,35 @@ const RegistrationPage = (props) => {
return;
}
+ if (executeRecaptcha) {
+ try {
+ recaptchaToken = await executeRecaptcha('submit_post');
+ if (!recaptchaToken) {
+ setCaptchaError(intl.formatMessage(messages['discussions.captcha.verification.label']));
+ return;
+ }
+ } catch (error) {
+ setCaptchaError(intl.formatMessage(messages['discussions.captcha.verification.label']));
+ return;
+ }
+ setCaptchaError('');
+ }
// Preparing payload for submission
- payload = prepareRegistrationPayload(
- payload,
- configurableFormFields,
- flags.showMarketingEmailOptInCheckbox,
- totalRegistrationTime,
- queryParams);
+ if (recaptchaToken) {
+ payload = prepareRegistrationPayload(
+ payload,
+ configurableFormFields,
+ flags.showMarketingEmailOptInCheckbox,
+ totalRegistrationTime,
+ queryParams);
- // making register call
- dispatch(registerNewUser(payload));
+ const updatedpayload = {
+ ...payload,
+ captcha_token: recaptchaToken,
+ };
+ // making register call
+ dispatch(registerNewUser(updatedpayload));
+ }
};
const handleSubmit = (e) => {
@@ -393,8 +417,12 @@ const RegistrationPage = (props) => {
setFormFields={setConfigurableFormFields}
autoSubmitRegisterForm={autoSubmitRegForm}
fieldDescriptions={fieldDescriptions}
- countriesCodesList={countriesCodesList}
/>
+ {captchaError && (
+
+ {captchaError}
+
+ )}