Fix password update (#71)

* Fix password update
* Use next or callback params for retro-compatibility
This commit is contained in:
Giancarlo Buomprisco
2024-10-12 05:09:34 +02:00
committed by GitHub
parent 2fe8fc0231
commit 25adc2d1e3
6 changed files with 80 additions and 9 deletions

View File

@@ -80,4 +80,10 @@ export class AuthPageObject {
await this.visitConfirmEmailLink(email); await this.visitConfirmEmailLink(email);
} }
async updatePassword(password: string) {
await this.page.fill('[name="password"]', password);
await this.page.fill('[name="repeatPassword"]', password);
await this.page.click('[type="submit"]');
}
} }

View File

@@ -0,0 +1,48 @@
import { expect, test } from '@playwright/test';
import { AuthPageObject } from './auth.po';
const email = 'owner@makerkit.dev';
const newPassword = (Math.random() * 10000).toString();
test.describe('Password Reset Flow', () => {
test.describe.configure({ mode: 'serial' });
test('will reset the password and sign in with new one', async ({ page }) => {
const auth = new AuthPageObject(page);
await page.goto('/auth/password-reset');
await page.fill('[name="email"]', email);
await page.click('[type="submit"]');
await auth.visitConfirmEmailLink(email);
await page.waitForURL('/update-password');
await auth.updatePassword(newPassword);
await page
.locator('a', {
hasText: 'Back to Home Page',
})
.click();
await page.waitForURL('/home');
await auth.signOut();
await page
.locator('a', {
hasText: 'Sign in',
})
.click();
await auth.signIn({
email,
password: newPassword,
});
await page.waitForURL('/home');
});
});

View File

@@ -15,11 +15,13 @@ export const generateMetadata = async () => {
}; };
}; };
const Logo = () => <AppLogo href={''} />;
async function UpdatePasswordPage() { async function UpdatePasswordPage() {
await requireUserInServerComponent(); await requireUserInServerComponent();
return ( return (
<AuthLayoutShell Logo={AppLogo}> <AuthLayoutShell Logo={Logo}>
<UpdatePasswordForm redirectTo={pathsConfig.app.home} /> <UpdatePasswordForm redirectTo={pathsConfig.app.home} />
</AuthLayoutShell> </AuthLayoutShell>
); );

View File

@@ -20,6 +20,7 @@ import { If } from '@kit/ui/if';
import { Input } from '@kit/ui/input'; import { Input } from '@kit/ui/input';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import { useCaptchaToken } from '../captcha/client';
import { AuthErrorAlert } from './auth-error-alert'; import { AuthErrorAlert } from './auth-error-alert';
const PasswordResetSchema = z.object({ const PasswordResetSchema = z.object({
@@ -31,6 +32,8 @@ export function PasswordResetRequestContainer(params: {
}) { }) {
const { t } = useTranslation('auth'); const { t } = useTranslation('auth');
const resetPasswordMutation = useRequestResetPassword(); const resetPasswordMutation = useRequestResetPassword();
const { captchaToken, resetCaptchaToken } = useCaptchaToken();
const error = resetPasswordMutation.error; const error = resetPasswordMutation.error;
const success = resetPasswordMutation.data; const success = resetPasswordMutation.data;
@@ -55,11 +58,20 @@ export function PasswordResetRequestContainer(params: {
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(({ email }) => { onSubmit={form.handleSubmit(({ email }) => {
return resetPasswordMutation.mutateAsync({ const redirectTo = new URL(
email, params.redirectPath,
redirectTo: new URL(params.redirectPath, window.location.origin) window.location.origin,
.href, ).href;
});
return resetPasswordMutation
.mutateAsync({
email,
redirectTo,
captchaToken,
})
.catch(() => {
resetCaptchaToken();
});
})} })}
className={'w-full'} className={'w-full'}
> >

View File

@@ -48,11 +48,12 @@ class AuthCallbackService {
const token_hash = searchParams.get('token_hash'); const token_hash = searchParams.get('token_hash');
const type = searchParams.get('type') as EmailOtpType | null; const type = searchParams.get('type') as EmailOtpType | null;
const callbackParam = searchParams.get('callback'); const callbackParam = searchParams.get('next') ?? searchParams.get('callback');
let nextPath: string | null = null; let nextPath: string | null = null;
const callbackUrl = callbackParam ? new URL(callbackParam) : null; const callbackUrl = callbackParam ? new URL(callbackParam) : null;
// if we have a callback url, we check if it has a next path
if (callbackUrl) { if (callbackUrl) {
// if we have a callback url, we check if it has a next path // if we have a callback url, we check if it has a next path
const callbackNextPath = callbackUrl.searchParams.get('next'); const callbackNextPath = callbackUrl.searchParams.get('next');

View File

@@ -2,9 +2,10 @@ import { useMutation } from '@tanstack/react-query';
import { useSupabase } from './use-supabase'; import { useSupabase } from './use-supabase';
interface Params { interface RequestPasswordResetMutationParams {
email: string; email: string;
redirectTo: string; redirectTo: string;
captchaToken?: string;
} }
/** /**
@@ -18,11 +19,12 @@ export function useRequestResetPassword() {
const client = useSupabase(); const client = useSupabase();
const mutationKey = ['auth', 'reset-password']; const mutationKey = ['auth', 'reset-password'];
const mutationFn = async (params: Params) => { const mutationFn = async (params: RequestPasswordResetMutationParams) => {
const { error, data } = await client.auth.resetPasswordForEmail( const { error, data } = await client.auth.resetPasswordForEmail(
params.email, params.email,
{ {
redirectTo: params.redirectTo, redirectTo: params.redirectTo,
captchaToken: params.captchaToken,
}, },
); );