Skip to content

Commit

Permalink
feat: Implement reCAPTCHA (#1362)
Browse files Browse the repository at this point in the history
  • Loading branch information
maciaszczykm authored Sep 18, 2024
1 parent 6f1ef89 commit 7c7985c
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 8 deletions.
1 change: 1 addition & 0 deletions www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"react-dom": "18.3.1",
"react-file-icon": "1.3.0",
"react-file-picker": "0.0.6",
"react-google-recaptcha-v3": "1.10.1",
"react-icons": "4.9.0",
"react-json-view": "1.21.3",
"react-multiline-clamp": "2.0.0",
Expand Down
49 changes: 45 additions & 4 deletions www/src/components/users/MagicLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import { useApolloClient } from '@apollo/client'
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
import queryString from 'query-string'
import { A, Button, Div, Flex, Icon } from 'honorable'

import styled from 'styled-components'
import {
GoogleReCaptchaProvider,
useGoogleReCaptcha,
} from 'react-google-recaptcha-v3'
import styled, { useTheme } from 'styled-components'

import {
AcceptLoginDocument,
Expand Down Expand Up @@ -237,6 +240,19 @@ const setInputFocus = (ref: RefObject<any>) => {
}

export function Login() {
return (
<GoogleReCaptchaProvider
reCaptchaKey="6LcKLkQqAAAAABfRRfhQGp3c0rcytvDzcx8cddcq"
language="en"
>
<LoginInternal />
</GoogleReCaptchaProvider>
)
}

function LoginInternal() {
const theme = useTheme()
const navigate = useNavigate()
const [state, setState] = useState<LoginState>(State.Initial)
const prevState = useRef<LoginState>(State.Initial)
const history = useHistory()
Expand All @@ -249,10 +265,21 @@ export function Login() {
)
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const navigate = useNavigate()
const passwordRef = useRef<HTMLElement>()
const emailRef = useRef<HTMLElement>()

const { executeRecaptcha } = useGoogleReCaptcha()
const [captcha, setCaptcha] = useState('')
const handleReCaptchaVerify = useCallback(async () => {
if (!executeRecaptcha) return

setCaptcha(await executeRecaptcha('login'))
}, [executeRecaptcha, setCaptcha])

useEffect(() => {
handleReCaptchaVerify()
}, [handleReCaptchaVerify])

useEffect(() => {
setInputFocus(emailRef)
}, [])
Expand All @@ -277,6 +304,7 @@ export function Login() {
email,
password,
deviceToken: typeof deviceToken === 'string' ? deviceToken : undefined,
captcha,
},
onCompleted: ({ login }) => {
setToken(login?.jwt)
Expand Down Expand Up @@ -386,7 +414,7 @@ export function Login() {
state === State.PwdLogin_CheckingPwd ||
state === State.PwdLogin_CheckPwd
const disableSubmit = isPasswordLogin
? password.length === 0
? password.length === 0 || !captcha
: !isValidEmail(email)

const onSubmit = useCallback(
Expand Down Expand Up @@ -499,6 +527,19 @@ export function Login() {
placeholder="Enter password"
/>
</Collapsible>
{!captcha && (
<div
css={{
...theme.partials.text.inlineLink,
textAlign: 'end',
cursor: 'pointer',
marginBottom: theme.spacing.xsmall,
}}
onClick={handleReCaptchaVerify}
>
verify reCAPTCHA
</div>
)}
<Button
type="submit"
width="100%"
Expand Down
11 changes: 9 additions & 2 deletions www/src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6011,6 +6011,7 @@ export type LoginMutationVariables = Exact<{
email: Scalars['String']['input'];
password: Scalars['String']['input'];
deviceToken?: InputMaybe<Scalars['String']['input']>;
captcha?: InputMaybe<Scalars['String']['input']>;
}>;


Expand Down Expand Up @@ -10777,8 +10778,13 @@ export type DevLoginMutationHookResult = ReturnType<typeof useDevLoginMutation>;
export type DevLoginMutationResult = Apollo.MutationResult<DevLoginMutation>;
export type DevLoginMutationOptions = Apollo.BaseMutationOptions<DevLoginMutation, DevLoginMutationVariables>;
export const LoginDocument = gql`
mutation Login($email: String!, $password: String!, $deviceToken: String) {
login(email: $email, password: $password, deviceToken: $deviceToken) {
mutation Login($email: String!, $password: String!, $deviceToken: String, $captcha: String) {
login(
email: $email
password: $password
deviceToken: $deviceToken
captcha: $captcha
) {
jwt
}
}
Expand All @@ -10801,6 +10807,7 @@ export type LoginMutationFn = Apollo.MutationFunction<LoginMutation, LoginMutati
* email: // value for 'email'
* password: // value for 'password'
* deviceToken: // value for 'deviceToken'
* captcha: // value for 'captcha'
* },
* });
*/
Expand Down
14 changes: 12 additions & 2 deletions www/src/graph/users.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,18 @@ mutation DevLogin {
}
}

mutation Login($email: String!, $password: String!, $deviceToken: String) {
login(email: $email, password: $password, deviceToken: $deviceToken) {
mutation Login(
$email: String!
$password: String!
$deviceToken: String
$captcha: String
) {
login(
email: $email
password: $password
deviceToken: $deviceToken
captcha: $captcha
) {
jwt
}
}
Expand Down
13 changes: 13 additions & 0 deletions www/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15587,6 +15587,18 @@ __metadata:
languageName: node
linkType: hard

"react-google-recaptcha-v3@npm:1.10.1":
version: 1.10.1
resolution: "react-google-recaptcha-v3@npm:1.10.1"
dependencies:
hoist-non-react-statics: ^3.3.2
peerDependencies:
react: ^16.3 || ^17.0 || ^18.0
react-dom: ^17.0 || ^18.0
checksum: b5ffe0bc0b13ad9ab2fa7b7acd979c7ff9d4f9d698412eaf8721167883a2ace2b4f3f5386c4b70f59f1cab9f062c368c360dbaa1b2cad4f1645795f3dbdf9355
languageName: node
linkType: hard

"react-icons@npm:4.9.0":
version: 4.9.0
resolution: "react-icons@npm:4.9.0"
Expand Down Expand Up @@ -19908,6 +19920,7 @@ __metadata:
react-error-boundary: 4.0.11
react-file-icon: 1.3.0
react-file-picker: 0.0.6
react-google-recaptcha-v3: 1.10.1
react-icons: 4.9.0
react-json-view: 1.21.3
react-multiline-clamp: 2.0.0
Expand Down

0 comments on commit 7c7985c

Please sign in to comment.