fix: handle duplicate email sign-up and null user in protected layout

- Register action now returns success instead of redirecting to /account/setup
  so users without a session (duplicate email with requireEmailVerification)
  see a 'check your email' message instead of crashing
- Protected layout guards against null/undefined locals.user before querying
  Prisma (was passing id: undefined)
- Updated register page to show success banner with verification instructions
- Updated test to match new action response
This commit is contained in:
NeilClack
2026-07-07 09:05:29 -05:00
parent 667ae4e05e
commit fc2790f6fa
4 changed files with 22 additions and 12 deletions
+5 -1
View File
@@ -3,8 +3,12 @@ import { error, redirect } from '@sveltejs/kit';
import { prisma } from '$lib/db';
export const load: LayoutServerLoad = async ({ locals }) => {
if (!locals.user?.id) {
throw error(403, 'not authorized');
}
const user = await prisma.user.findUnique({
where: { id: locals.user?.id },
where: { id: locals.user.id },
select: { id: true, roleId: true, profile: { select: { id: true } } }
});
if (!user) {
+3 -6
View File
@@ -1,6 +1,6 @@
import type { Actions } from './$types';
import { auth } from '$lib/auth';
import { fail, isRedirect, redirect } from '@sveltejs/kit';
import { fail } from '@sveltejs/kit';
export const actions: Actions = {
default: async ({ request }) => {
@@ -17,7 +17,7 @@ export const actions: Actions = {
}
try {
const response = await auth.api.signUpEmail({
await auth.api.signUpEmail({
body: {
name: `${firstName} ${lastName}`,
email: email,
@@ -25,11 +25,8 @@ export const actions: Actions = {
}
});
if (response.user) {
throw redirect(303, '/account/setup');
}
return { success: true };
} catch (err) {
if (isRedirect(err)) throw err;
const message = err instanceof Error ? err.message : 'An unexpected error occurred';
return fail(400, { error: message });
}
+11 -1
View File
@@ -8,7 +8,17 @@
<Window>
<div class="flex flex-col items-start justify-center">
{#if form?.error}
{#if form?.success}
<div
class="mb-4 w-full rounded-lg border border-green-200 bg-green-50 p-4 text-green-700"
>
<p class="font-medium">Account created!</p>
<p class="mt-1 text-sm">
We've sent a verification email. Check your inbox and click the link to verify your
email address, then log in.
</p>
</div>
{:else if form?.error}
<div class="mb-4 w-full rounded-lg border border-red-200 bg-red-50 p-3 text-red-700">
{form.error}
</div>
+3 -4
View File
@@ -41,7 +41,7 @@ describe('register page server action', () => {
expect(signUpEmail).not.toHaveBeenCalled();
});
it('redirects to /account/setup on successful signup', async () => {
it('returns success on successful signup', async () => {
signUpEmail.mockResolvedValue({ user: { id: 'user-1' } });
const request = createFormRequest({
@@ -52,9 +52,8 @@ describe('register page server action', () => {
password2: 'secret123'
});
await expect(actions.default({ request } as any)).rejects.toMatchObject({
status: 303
});
const result = await invokeDefault(request);
expect(result).toEqual({ success: true });
});
it('returns fail(400) when signUpEmail throws', async () => {