From 403db86e8c7d1049247947b782b444aed5aa2c3e Mon Sep 17 00:00:00 2001 From: spaenleh Date: Mon, 27 Mar 2023 10:44:18 +0200 Subject: [PATCH 01/12] feat: add recaptcha --- README.md | 13 +++++ package.json | 2 +- public/index.html | 2 + src/components/Root.tsx | 7 ++- src/components/SignIn.tsx | 14 ++++-- src/components/SignUp.tsx | 8 ++- src/config/constants.ts | 2 + src/context/RecaptchaContext.tsx | 83 ++++++++++++++++++++++++++++++++ yarn.lock | 19 +++++++- 9 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 src/context/RecaptchaContext.tsx diff --git a/README.md b/README.md index 3c5cf565..eadb2c11 100644 --- a/README.md +++ b/README.md @@ -1 +1,14 @@ # graasp-auth + +Create an `.env.local` file with: + +```sh +REACT_APP_API_HOST=http://localhost:3000 +PORT=3001 +REACT_APP_DOMAIN=localhost:3001 +REACT_APP_SHOW_NOTIFICATIONS=true + +REACT_APP_RECAPTCHA_SITE_KEY= +``` + +Generate your recaptcha key from [the reCAPTCHA admin console](https://www.google.com/recaptcha/admin/create) diff --git a/package.json b/package.json index 9dd4bcdf..17f2ef0e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@emotion/react": "11.10.5", "@emotion/styled": "11.10.5", "@graasp/query-client": "0.2.0", - "@graasp/sdk": "0.4.1", + "@graasp/sdk": "github:graasp/graasp-sdk#122-add-actiontype-for-recaptcha", "@graasp/translations": "1.8.0", "@graasp/ui": "0.11.1", "@mui/icons-material": "5.11.0", diff --git a/public/index.html b/public/index.html index 48f74c86..36ac050e 100644 --- a/public/index.html +++ b/public/index.html @@ -27,6 +27,8 @@ href="%PUBLIC_URL%/favicon-16x16.png" /> + + diff --git a/src/components/SuccessContent.tsx b/src/components/SuccessContent.tsx index ae38b4f6..3c4dc10b 100644 --- a/src/components/SuccessContent.tsx +++ b/src/components/SuccessContent.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { Trans } from 'react-i18next'; import { MUTATION_KEYS } from '@graasp/query-client'; +import { RecaptchaAction } from '@graasp/sdk'; import { AUTH, namespaces } from '@graasp/translations'; import { Button } from '@graasp/ui'; @@ -12,6 +13,7 @@ import { useAuthTranslation } from '../config/i18n'; import { useMutation } from '../config/queryClient'; import { SUCCESS_CONTENT_ID } from '../config/selectors'; import { BACK_BUTTON_ID, RESEND_EMAIL_BUTTON_ID } from '../config/selectors'; +import { useRecaptcha } from '../context/RecaptchaContext'; type Props = { title: string; @@ -25,17 +27,21 @@ const SuccessContent = ({ handleBackButtonClick = null, }: Props) => { const { t } = useAuthTranslation(); + const { executeCaptcha } = useRecaptcha(); const [isEmailSent, setIsEmailSent] = useState(false); // used for resend email - const { mutate: signIn } = useMutation( - MUTATION_KEYS.SIGN_IN, - ); + const { mutate: signIn } = useMutation< + unknown, + unknown, + { email: string; captcha: string } + >(MUTATION_KEYS.SIGN_IN); // used for resend email const handleResendEmail = async () => { const lowercaseEmail = email.toLowerCase(); - signIn({ email: lowercaseEmail }); + const token = await executeCaptcha(RecaptchaAction.SignInWithPassword); + signIn({ email: lowercaseEmail, captcha: token }); }; const onClickResendEmail = () => { From a8b140710cc56ce19506d83a6f203a45e5304b3e Mon Sep 17 00:00:00 2001 From: spaenleh Date: Mon, 27 Mar 2023 16:48:11 +0200 Subject: [PATCH 11/12] feat: add sentry tracing --- .github/workflows/cdelivery-s3-caller.yml | 1 + .github/workflows/cdeployment-s3-caller.yml | 1 + .github/workflows/cintegration-s3-caller.yml | 1 + package.json | 6 +- src/components/App.tsx | 22 ++-- src/components/ErrorFallback.tsx | 11 ++ src/components/SuccessContent.tsx | 2 +- src/config/constants.ts | 3 + src/config/i18n.ts | 1 + src/index.tsx | 32 +++++- yarn.lock | 107 ++++++++++++++++--- 11 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 src/components/ErrorFallback.tsx diff --git a/.github/workflows/cdelivery-s3-caller.yml b/.github/workflows/cdelivery-s3-caller.yml index d1cfb0b4..992b842d 100644 --- a/.github/workflows/cdelivery-s3-caller.yml +++ b/.github/workflows/cdelivery-s3-caller.yml @@ -26,4 +26,5 @@ jobs: graasp-explorer-host: ${{ secrets.REACT_APP_GRAASP_EXPLORE_HOST_STAGE }} authentication-host: ${{ secrets.REACT_APP_AUTHENTICATION_HOST_STAGE }} recaptcha-site-key: ${{ secrets.REACT_APP_RECAPTCHA_SITE_KEY }} + sentry-dsn: ${{ secrets.REACT_APP_SENTRY_DSN }} domain: ${{ secrets.REACT_APP_DOMAIN_STAGE }} diff --git a/.github/workflows/cdeployment-s3-caller.yml b/.github/workflows/cdeployment-s3-caller.yml index 677bbe45..73b37f14 100644 --- a/.github/workflows/cdeployment-s3-caller.yml +++ b/.github/workflows/cdeployment-s3-caller.yml @@ -32,4 +32,5 @@ jobs: graasp-explorer-host: ${{ secrets.REACT_APP_GRAASP_EXPLORE_HOST_PROD }} authentication-host: ${{ secrets.REACT_APP_AUTHENTICATION_HOST_PROD }} recaptcha-site-key: ${{ secrets.REACT_APP_RECAPTCHA_SITE_KEY }} + sentry-dsn: ${{ secrets.REACT_APP_SENTRY_DSN }} domain: ${{ secrets.REACT_APP_DOMAIN_PROD }} diff --git a/.github/workflows/cintegration-s3-caller.yml b/.github/workflows/cintegration-s3-caller.yml index cee6d508..4f414ccd 100644 --- a/.github/workflows/cintegration-s3-caller.yml +++ b/.github/workflows/cintegration-s3-caller.yml @@ -30,4 +30,5 @@ jobs: graasp-compose-host: ${{ secrets.REACT_APP_GRAASP_COMPOSE_HOST_DEV }} graasp-explorer-host: ${{ secrets.REACT_APP_GRAASP_EXPLORE_HOST_DEV }} recaptcha-site-key: ${{ secrets.REACT_APP_RECAPTCHA_SITE_KEY }} + sentry-dsn: ${{ secrets.REACT_APP_SENTRY_DSN }} domain: ${{ secrets.REACT_APP_DOMAIN_DEV }} diff --git a/package.json b/package.json index fd8d1893..0bc80f3b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "@mui/icons-material": "5.11.0", "@mui/lab": "5.0.0-alpha.119", "@mui/material": "5.11.8", + "@sentry/browser": "7.45.0", + "@sentry/react": "7.45.0", + "@sentry/tracing": "7.45.0", "http-status-codes": "2.2.0", "qs": "6.11.0", "react": "17.0.2", @@ -107,7 +110,8 @@ "wait-on": "7.0.1" }, "resolutions": { - "@graasp/sdk": "0.10.0" + "@graasp/sdk": "0.10.0", + "@types/react": "17.0.30" }, "packageManager": "yarn@3.2.1" } diff --git a/src/components/App.tsx b/src/components/App.tsx index f4dde17f..a7af3418 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,20 +1,24 @@ +import * as Sentry from '@sentry/react'; import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'; import { HOME_PATH, SIGN_UP_PATH, buildSignInPath } from '../config/paths'; +import ErrorFallback from './ErrorFallback'; import Redirection from './Redirection'; import SignIn from './SignIn'; import SignUp from './SignUp'; const App = () => ( - - - - } /> - } /> - } /> - - - + } showDialog> + + + + } /> + } /> + } /> + + + + ); export default App; diff --git a/src/components/ErrorFallback.tsx b/src/components/ErrorFallback.tsx new file mode 100644 index 00000000..35b2e279 --- /dev/null +++ b/src/components/ErrorFallback.tsx @@ -0,0 +1,11 @@ +import { FAILURE_MESSAGES } from '@graasp/translations'; + +import { Alert } from '@mui/material'; + +import { useMessagesTranslation } from '../config/i18n'; + +const ErrorFallback = () => { + const { t } = useMessagesTranslation(); + return {t(FAILURE_MESSAGES.UNEXPECTED_ERROR)}; +}; +export default ErrorFallback; diff --git a/src/components/SuccessContent.tsx b/src/components/SuccessContent.tsx index 3c4dc10b..b9c38809 100644 --- a/src/components/SuccessContent.tsx +++ b/src/components/SuccessContent.tsx @@ -40,7 +40,7 @@ const SuccessContent = ({ // used for resend email const handleResendEmail = async () => { const lowercaseEmail = email.toLowerCase(); - const token = await executeCaptcha(RecaptchaAction.SignInWithPassword); + const token = await executeCaptcha(RecaptchaAction.SignIn); signIn({ email: lowercaseEmail, captcha: token }); }; diff --git a/src/config/constants.ts b/src/config/constants.ts index 474ddc11..81a0f842 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -19,6 +19,9 @@ export const GRAASP_COMPOSE_HOST = export const AUTHENTICATION_HOST = process.env.REACT_APP_AUTHENTICATION_HOST || 'http://localhost:3001'; +export const SENRY_DSN = process.env.REACT_APP_SENTRY_DSN; +export const APP_VERSION = process.env.REACT_APP_VERSION; + export const RECAPTCHA_SITE_KEY = process.env.REACT_APP_RECAPTCHA_SITE_KEY; export const NAME_MAXIMUM_LENGTH = 300; diff --git a/src/config/i18n.ts b/src/config/i18n.ts index bee7f383..9900e9ff 100644 --- a/src/config/i18n.ts +++ b/src/config/i18n.ts @@ -8,5 +8,6 @@ i18n.use(initReactI18next); export const useAuthTranslation = () => useTranslation(namespaces.auth); export const useBuilderTranslation = () => useTranslation(namespaces.builder); export const useCommonTranslation = () => useTranslation(namespaces.common); +export const useMessagesTranslation = () => useTranslation(namespaces.messages); export default i18n; diff --git a/src/index.tsx b/src/index.tsx index 717566e5..4f95acfb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,18 @@ +import * as Sentry from '@sentry/react'; +import { BrowserTracing } from '@sentry/tracing'; import { render } from 'react-dom'; import ReactGA from 'react-ga4'; import { hasAcceptedCookies } from '@graasp/sdk'; import Root from './components/Root'; -import { GA_MEASUREMENT_ID, NODE_ENV } from './config/constants'; +import { + APP_VERSION, + DOMAIN, + GA_MEASUREMENT_ID, + NODE_ENV, + SENRY_DSN, +} from './config/constants'; import './index.css'; import './index.css'; @@ -13,5 +21,27 @@ if (GA_MEASUREMENT_ID && hasAcceptedCookies() && NODE_ENV !== 'test') { ReactGA.send('pageview'); } +Sentry.init({ + dsn: SENRY_DSN, + integrations: [ + new BrowserTracing(), + new Sentry.Replay({ + maskAllText: true, + blockAllMedia: true, + }), + ], + release: APP_VERSION, + environment: DOMAIN, + tracesSampleRate: 1.0, + + // This sets the sample rate to be 10%. You may want this to be 100% while + // in development and sample at a lower rate in production + replaysSessionSampleRate: 0.1, + + // If the entire session is not sampled, use the below sample rate to sample + // sessions when an error occurs. + replaysOnErrorSampleRate: 1.0, +}); + const root = document.getElementById('root'); render(, root); diff --git a/yarn.lock b/yarn.lock index 312b833c..b873b7b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3207,6 +3207,95 @@ __metadata: languageName: node linkType: hard +"@sentry-internal/tracing@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry-internal/tracing@npm:7.45.0" + dependencies: + "@sentry/core": 7.45.0 + "@sentry/types": 7.45.0 + "@sentry/utils": 7.45.0 + tslib: ^1.9.3 + checksum: 64cb8ddca80d4c3a8b7d7fab8ebfc38e5eeb56753b72fc7fbcc6003a190f0492fe89075f7e7d7a2621f4746c52613c79eec2695368cc44da134b0be42bd0f71b + languageName: node + linkType: hard + +"@sentry/browser@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/browser@npm:7.45.0" + dependencies: + "@sentry-internal/tracing": 7.45.0 + "@sentry/core": 7.45.0 + "@sentry/replay": 7.45.0 + "@sentry/types": 7.45.0 + "@sentry/utils": 7.45.0 + tslib: ^1.9.3 + checksum: 6f9328e32fbe92a31e891ec00f6fbd72397d6e6f9a53ec4025da8682bcb484fda49ad05ee73a95b1f0554f9a4543d9d5f40e26273eb9c70617a475939dfd29ad + languageName: node + linkType: hard + +"@sentry/core@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/core@npm:7.45.0" + dependencies: + "@sentry/types": 7.45.0 + "@sentry/utils": 7.45.0 + tslib: ^1.9.3 + checksum: c0453edf833b7ed86bcde6d341090bd3f4cfdb08b50a5e96cbe9d240d3955ea90c4af5550af37f95e8b987c13fa178cb004dd7bea9071145e63ce0dc7c635ec6 + languageName: node + linkType: hard + +"@sentry/react@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/react@npm:7.45.0" + dependencies: + "@sentry/browser": 7.45.0 + "@sentry/types": 7.45.0 + "@sentry/utils": 7.45.0 + hoist-non-react-statics: ^3.3.2 + tslib: ^1.9.3 + peerDependencies: + react: 15.x || 16.x || 17.x || 18.x + checksum: 2b2a3014f85f693ac8c30af8e86e0fa843805ad123c9093d16bc89a4299f2c9942997145db8350421893e6bac3aaf18bee1633ec7a012ca02e82c07f3ddf4485 + languageName: node + linkType: hard + +"@sentry/replay@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/replay@npm:7.45.0" + dependencies: + "@sentry/core": 7.45.0 + "@sentry/types": 7.45.0 + "@sentry/utils": 7.45.0 + checksum: 6e960f50cb1b3fae97482edfc2458542a254af0060481e369740c1e9f93093ca662f130c400fafff092e75079929c21f59ea78d85a095b59a07250976d478b84 + languageName: node + linkType: hard + +"@sentry/tracing@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/tracing@npm:7.45.0" + dependencies: + "@sentry-internal/tracing": 7.45.0 + checksum: 21cfa98152f66c588124b368cc2883688e4d12a6a1d3a48519522b4d165ba56f8b72d2154686e278ecedc7b6afe5c6f469d256a31745fa9b8140a1040064e9f5 + languageName: node + linkType: hard + +"@sentry/types@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/types@npm:7.45.0" + checksum: feb7de474f486f52f8f2594dc0559872c51c770b8511d90383eb4aadac8dffc2ddfb8fd7747cfd1486e629eba47bc7c9fae72fdad28cddaaeaf8dd1a52fabdfa + languageName: node + linkType: hard + +"@sentry/utils@npm:7.45.0": + version: 7.45.0 + resolution: "@sentry/utils@npm:7.45.0" + dependencies: + "@sentry/types": 7.45.0 + tslib: ^1.9.3 + checksum: 5d4d2ab4fe85d7231a35859cbeecc3d0c95368c818f15d8d6332f8c57604a67c0e12d5a81c23e5dcb1c53b4e44faf753028592792eb98e4af1440ee375b2292e + languageName: node + linkType: hard + "@sideway/address@npm:^4.1.3": version: 4.1.4 resolution: "@sideway/address@npm:4.1.4" @@ -3919,17 +4008,6 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*": - version: 18.0.28 - resolution: "@types/react@npm:18.0.28" - dependencies: - "@types/prop-types": "*" - "@types/scheduler": "*" - csstype: ^3.0.2 - checksum: e752df961105e5127652460504785897ca6e77259e0da8f233f694f9e8f451cde7fa0709d4456ade0ff600c8ce909cfe29f9b08b9c247fa9b734e126ec53edd7 - languageName: node - linkType: hard - "@types/react@npm:17.0.30": version: 17.0.30 resolution: "@types/react@npm:17.0.30" @@ -9379,6 +9457,9 @@ __metadata: "@mui/icons-material": 5.11.0 "@mui/lab": 5.0.0-alpha.119 "@mui/material": 5.11.8 + "@sentry/browser": 7.45.0 + "@sentry/react": 7.45.0 + "@sentry/tracing": 7.45.0 "@testing-library/jest-dom": 5.16.4 "@testing-library/react": 13.2.0 "@testing-library/user-event": 14.2.0 @@ -9577,7 +9658,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.1": +"hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -16856,7 +16937,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1": +"tslib@npm:^1.8.1, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd From 8ec9421b624cbf616c9ccf47f47d9a7e71d52d4b Mon Sep 17 00:00:00 2001 From: spaenleh Date: Mon, 27 Mar 2023 17:16:34 +0200 Subject: [PATCH 12/12] fix: add trim to name field --- src/components/SignUp.tsx | 6 +++++- src/index.tsx | 8 +------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/SignUp.tsx b/src/components/SignUp.tsx index 7216a3a6..a52b1f45 100644 --- a/src/components/SignUp.tsx +++ b/src/components/SignUp.tsx @@ -83,7 +83,11 @@ const SignUp = () => { setShouldValidate(true); } else { const token = await executeCaptcha(RecaptchaAction.SignUp); - await signUp({ name, email: lowercaseEmail, captcha: token }); + await signUp({ + name: name.trim(), + email: lowercaseEmail, + captcha: token, + }); setSuccessView(true); } }; diff --git a/src/index.tsx b/src/index.tsx index 4f95acfb..eabdd0fb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,13 +23,7 @@ if (GA_MEASUREMENT_ID && hasAcceptedCookies() && NODE_ENV !== 'test') { Sentry.init({ dsn: SENRY_DSN, - integrations: [ - new BrowserTracing(), - new Sentry.Replay({ - maskAllText: true, - blockAllMedia: true, - }), - ], + integrations: [new BrowserTracing(), new Sentry.Replay()], release: APP_VERSION, environment: DOMAIN, tracesSampleRate: 1.0,