Fix reset PasswordToken (#6366)

## Bug Description

We are facing a bug in case recaptcha is enabled.
To reproduce:
- Create your recaptcha: https://www.google.com/recaptcha/about/
- update your server .env with the following variables:

```
CAPTCHA_SECRET_KEY=REPLACE_ME
CAPTCHA_SITE_KEY=REPLACE_ME
CAPTCHA_DRIVER=google-recaptcha
```

- Go to the login page, enter an existing user email and hit 'Reset your
password'.

- Add a console.log in emailPasswordResetLink in auth.resolver.ts to get
the token that would be sent by email if you don't have the mailer setup

- Browse: /reset-password/{passwordToken}

- Update the password:
<img width="1446" alt="image"
src="https://github.com/user-attachments/assets/dd5b077f-293e-451a-8630-22d24ac66c42">

- See that the token is invalid

You should see two calls in your developer network tab. A successful one
to update the password and another to log you in. This 2nd call
(Challenge) does not have the captcha token provided. It should be

## Fix

- Refreshing the token on page load
- providing it to the Challenge graphql call
This commit is contained in:
Charles Bochet
2024-07-22 17:36:31 +02:00
committed by GitHub
parent 01fe3b673e
commit c69d665114
4 changed files with 13 additions and 18 deletions

View File

@ -61,7 +61,7 @@
"test": {},
"storybook:build": {
"options": {
"env": { "NODE_OPTIONS": "--max_old_space_size=5000" }
"env": { "NODE_OPTIONS": "--max_old_space_size=6000" }
}
},
"storybook:serve:dev": {

View File

@ -165,7 +165,9 @@ export const PageChangeEffect = () => {
useEffect(() => {
if (
isCaptchaScriptLoaded &&
isMatchingLocation(AppPath.SignInUp || AppPath.Invite)
(isMatchingLocation(AppPath.SignInUp) ||
isMatchingLocation(AppPath.Invite) ||
isMatchingLocation(AppPath.ResetPassword))
) {
requestFreshCaptchaToken();
}

View File

@ -1,12 +1,12 @@
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { useNavigate, useParams } from 'react-router-dom';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { zodResolver } from '@hookform/resolvers/zod';
import { isNonEmptyString } from '@sniptt/guards';
import { motion } from 'framer-motion';
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { useNavigate, useParams } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { z } from 'zod';
@ -15,6 +15,7 @@ import { Title } from '@/auth/components/Title';
import { useAuth } from '@/auth/hooks/useAuth';
import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex';
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
import { AppPath } from '@/types/AppPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@ -118,6 +119,7 @@ export const PasswordReset = () => {
useUpdatePasswordViaResetTokenMutation();
const { signInWithCredentials } = useAuth();
const { readCaptchaToken } = useReadCaptchaToken();
const onSubmit = async (formData: Form) => {
try {
@ -143,7 +145,9 @@ export const PasswordReset = () => {
return;
}
await signInWithCredentials(email || '', formData.newPassword);
const token = await readCaptchaToken();
await signInWithCredentials(email || '', formData.newPassword, token);
navigate(AppPath.Index);
} catch (err) {
logError(err);

View File

@ -1,11 +0,0 @@
import { ArgsType, Field } from '@nestjs/graphql';
import { IsEmail, IsNotEmpty } from 'class-validator';
@ArgsType()
export class PasswordResetTokenInput {
@Field(() => String)
@IsNotEmpty()
@IsEmail()
email: string;
}