diff --git a/.gitignore b/.gitignore
index 4d29575..532eddc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+
+.env
\ No newline at end of file
diff --git a/env.example b/env.example
new file mode 100644
index 0000000..f1c1d4c
--- /dev/null
+++ b/env.example
@@ -0,0 +1 @@
+REACT_APP_RECAPTCHA_SITE_KEY=
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index bddf831..af185bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,7 @@
"react": "^18.2.0",
"react-countup": "^6.5.0",
"react-dom": "^18.2.0",
+ "react-google-recaptcha": "^3.1.0",
"react-i18next": "^13.0.1",
"react-microsoft-clarity": "^1.2.0",
"react-qr-scanner": "^1.0.0-alpha.11",
@@ -11180,6 +11181,19 @@
"he": "bin/he"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -18496,6 +18510,18 @@
"node": ">=14"
}
},
+ "node_modules/react-async-script": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
+ "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
+ "dependencies": {
+ "hoist-non-react-statics": "^3.3.0",
+ "prop-types": "^15.5.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.4.1"
+ }
+ },
"node_modules/react-countup": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.5.0.tgz",
@@ -18664,6 +18690,18 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
},
+ "node_modules/react-google-recaptcha": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
+ "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
+ "dependencies": {
+ "prop-types": "^15.5.0",
+ "react-async-script": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.4.1"
+ }
+ },
"node_modules/react-hook-form": {
"version": "7.48.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz",
diff --git a/package.json b/package.json
index bab02f4..5ea59a5 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"react": "^18.2.0",
"react-countup": "^6.5.0",
"react-dom": "^18.2.0",
+ "react-google-recaptcha": "^3.1.0",
"react-i18next": "^13.0.1",
"react-microsoft-clarity": "^1.2.0",
"react-qr-scanner": "^1.0.0-alpha.11",
diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json
index 5518dc6..456a8aa 100644
--- a/public/locales/de/translation.json
+++ b/public/locales/de/translation.json
@@ -62,7 +62,8 @@
"calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich",
"calendarMinEarliestBookingTimeRequired": "Minimaler frühester Buchungszeitpunkt ist erforderlich",
"companyNameRequired": "Firmenname ist erforderlich",
- "companyNameMinLength": "Firmenname muss mindestens {{minLength}} Zeichen lang sein"
+ "companyNameMinLength": "Firmenname muss mindestens {{minLength}} Zeichen lang sein",
+ "recaptchaRequired": "Bitte bestätigen Sie, dass Sie kein Roboter sind"
},
"request": {
"inputsInvalid": {
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 0127296..52ac929 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -62,7 +62,8 @@
"calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days",
"calendarMinEarliestBookingTimeRequired": "Please enter the min. earliest booking time",
"companyNameRequired": "Please enter the company name",
- "companyNameMinLength": "Company name must be at least {{minLength}} characters"
+ "companyNameMinLength": "Company name must be at least {{minLength}} characters",
+ "recaptchaRequired": "Please confirm that you are not a robot"
},
"request": {
"inputsInvalid": {
diff --git a/src/Pages/Authentication/index.js b/src/Pages/Authentication/index.js
index 64eb064..f67eccd 100644
--- a/src/Pages/Authentication/index.js
+++ b/src/Pages/Authentication/index.js
@@ -17,7 +17,7 @@ import {
showInputsInvalidNotification,
showUnkownErrorNotification,
} from "../../utils";
-import { useEffect, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import {
MyAccountNameFormInput,
MyCompanyNameFormInput,
@@ -27,6 +27,7 @@ import {
import { useTranslation } from "react-i18next";
import MyAppLogo from "../../Components/MyAppLogo";
import { useNavigate } from "react-router-dom";
+import ReCAPTCHA from "react-google-recaptcha";
export const AuthenticationMethod = {
LOGIN: 1,
@@ -95,6 +96,7 @@ function Login({ notificationApi }) {
const [isRequesting, setIsRequesting] = useState(false);
const [step, setStep] = useState(LoginStep.ACCOUNT_NAME);
+ const recaptchaValueRef = useRef(null);
const [form] = Form.useForm();
@@ -212,6 +214,21 @@ function Login({ notificationApi }) {