feat: added captcha on authn mfe

This commit is contained in:
sundasnoreen12
2025-09-01 12:52:29 +05:00
committed by Awais Ansari
parent cf3e8b5c7f
commit da0755467d
5 changed files with 87 additions and 35 deletions

13
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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 = () => (
<AppProvider store={configureStore()}>
<Helmet>
<link rel="shortcut icon" href={getConfig().FAVICON_URL} type="image/x-icon" />
</Helmet>
<Routes>
<Route path="/" element={<Navigate replace to={updatePathWithQueryParams(REGISTER_PAGE)} />} />
<Route
path={REGISTER_EMBEDDED_PAGE}
element={<EmbeddedRegistrationRoute><RegistrationPage /></EmbeddedRegistrationRoute>}
/>
<Route
path={LOGIN_PAGE}
element={
<UnAuthOnlyRoute><Logistration selectedPage={LOGIN_PAGE} /></UnAuthOnlyRoute>
}
/>
<Route path={REGISTER_PAGE} element={<UnAuthOnlyRoute><Logistration /></UnAuthOnlyRoute>} />
<Route path={RESET_PAGE} element={<UnAuthOnlyRoute><ForgotPasswordPage /></UnAuthOnlyRoute>} />
<Route path={PASSWORD_RESET_CONFIRM} element={<ResetPasswordPage />} />
<Route path={AUTHN_PROGRESSIVE_PROFILING} element={<ProgressiveProfiling />} />
<Route path={RECOMMENDATIONS} element={<RecommendationsPage />} />
<Route path={PAGE_NOT_FOUND} element={<NotFoundPage />} />
<Route path="*" element={<Navigate replace to={PAGE_NOT_FOUND} />} />
</Routes>
<RouteTracker />
<MainAppSlot />
<GoogleReCaptchaProvider
reCaptchaKey="6LeQu6QrAAAAADR7XB--kDIKyAsqJu9SO22Jiptd"
useEnterprise
>
<Helmet>
<link rel="shortcut icon" href={getConfig().FAVICON_URL} type="image/x-icon" />
</Helmet>
{getConfig().ZENDESK_KEY && <Zendesk />}
<Routes>
<Route path="/" element={<Navigate replace to={updatePathWithQueryParams(REGISTER_PAGE)} />} />
<Route
path={REGISTER_EMBEDDED_PAGE}
element={<EmbeddedRegistrationRoute><RegistrationPage /></EmbeddedRegistrationRoute>}
/>
<Route
path={LOGIN_PAGE}
element={
<UnAuthOnlyRoute><Logistration selectedPage={LOGIN_PAGE} /></UnAuthOnlyRoute>
}
/>
<Route path={REGISTER_PAGE} element={<UnAuthOnlyRoute><Logistration /></UnAuthOnlyRoute>} />
<Route path={RESET_PAGE} element={<UnAuthOnlyRoute><ForgotPasswordPage /></UnAuthOnlyRoute>} />
<Route path={PASSWORD_RESET_CONFIRM} element={<ResetPasswordPage />} />
<Route path={AUTHN_PROGRESSIVE_PROFILING} element={<ProgressiveProfiling />} />
<Route path={RECOMMENDATIONS} element={<RecommendationsPage />} />
<Route path={PAGE_NOT_FOUND} element={<NotFoundPage />} />
<Route path="*" element={<Navigate replace to={PAGE_NOT_FOUND} />} />
</Routes>
</GoogleReCaptchaProvider>
</AppProvider>
);

View File

@@ -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 && (
<div className="mt-3 pgn__form-text-invalid pgn__form-text">
{captchaError}
</div>
)}
<StatefulButton
id="register-user"
name="register-user"

View File

@@ -206,6 +206,11 @@ const messages = defineMessages({
defaultMessage: 'Did you mean',
description: 'Did you mean alert suggestion',
},
'discussions.captcha.verification.label': {
id: 'discussions.captcha.verification.label',
defaultMessage: 'CAPTCHA verification failed.',
description: 'CAPTCHA verification failed',
},
});
export default messages;