Fix reset-password page to look up the reset token correctly

This commit is contained in:
NeilClack
2026-09-10 13:42:36 -05:00
parent 5ec3018f8b
commit ae8d661364
3 changed files with 27 additions and 7 deletions
+5 -1
View File
@@ -10,9 +10,11 @@ export const load: PageServerLoad = async ({ url }) => {
throw redirect(302, '/'); throw redirect(302, '/');
} }
// Better Auth stores reset tokens as identifier `reset-password:<token>`
// with the user id in `value`.
const verification = await prisma.verification.findFirst({ const verification = await prisma.verification.findFirst({
where: { where: {
value: token, identifier: `reset-password:${token}`,
expiresAt: { gt: new Date() } expiresAt: { gt: new Date() }
} }
}); });
@@ -20,6 +22,8 @@ export const load: PageServerLoad = async ({ url }) => {
if (!verification) { if (!verification) {
throw redirect(302, '/'); throw redirect(302, '/');
} }
return { invite: url.searchParams.get('invite') === '1' };
}; };
export const actions: Actions = { export const actions: Actions = {
+9 -2
View File
@@ -2,7 +2,7 @@
import type { PageProps } from './$types'; import type { PageProps } from './$types';
import Window from '$lib/components/containers/Window.svelte'; import Window from '$lib/components/containers/Window.svelte';
let { form }: PageProps = $props(); let { data, form }: PageProps = $props();
</script> </script>
<Window> <Window>
@@ -13,7 +13,14 @@
{/if} {/if}
<form method="POST" class="flex flex-col gap-4"> <form method="POST" class="flex flex-col gap-4">
<h1 class="text-2xl font-bold text-slate-900">Reset Password</h1> <h1 class="text-2xl font-bold text-slate-900">
{data.invite ? 'Set Your Password' : 'Reset Password'}
</h1>
{#if data.invite}
<p class="text-sm text-slate-600">
Welcome! Choose a password to finish setting up your account.
</p>
{/if}
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="password" class="text-sm font-medium text-slate-700"> New Password </label> <label for="password" class="text-sm font-medium text-slate-700"> New Password </label>
+13 -4
View File
@@ -56,19 +56,28 @@ describe('reset-password page server load', () => {
} }
expect(findFirst).toHaveBeenCalledWith({ expect(findFirst).toHaveBeenCalledWith({
where: { where: {
value: 'invalid-token', identifier: 'reset-password:invalid-token',
expiresAt: { gt: expect.any(Date) } expiresAt: { gt: expect.any(Date) }
} }
}); });
}); });
it('returns undefined when token is valid (verification found)', async () => { it('returns invite=false when token is valid', async () => {
findFirst.mockResolvedValue({ id: 'v-1', value: 'valid-token' }); findFirst.mockResolvedValue({ id: 'v-1', identifier: 'reset-password:valid-token' });
const url = createUrl('/reset-password', { token: 'valid-token' }); const url = createUrl('/reset-password', { token: 'valid-token' });
const result = await load({ url } as any); const result = await load({ url } as any);
expect(result).toBeUndefined(); expect(result).toEqual({ invite: false });
});
it('returns invite=true for registration links', async () => {
findFirst.mockResolvedValue({ id: 'v-1', identifier: 'reset-password:valid-token' });
const url = createUrl('/reset-password', { token: 'valid-token', invite: '1' });
const result = await load({ url } as any);
expect(result).toEqual({ invite: true });
}); });
it('redirects to / when verification is expired', async () => { it('redirects to / when verification is expired', async () => {