From c61129c82802e208b1e51412c88ac968cf4079f5 Mon Sep 17 00:00:00 2001 From: WOBBLEFANG THE THIRD Date: Mon, 1 Dec 2025 21:42:47 +0100 Subject: [PATCH] feat: enhance user signup process with password hashing and session management --- src/lib/server/db/schema.ts | 1 + src/routes/signup/+page.server.ts | 92 ++++++++++++++++++++++++++----- src/routes/signup/+page.svelte | 11 +++- 3 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 1d37f1a..ed8579b 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -5,6 +5,7 @@ export const userTable = pgTable('user', { name: text('name').notNull(), email: text('email').notNull().unique(), password: text('password').notNull(), + salt: text('salt').notNull(), created_at: timestamp('created_at').defaultNow() }); diff --git a/src/routes/signup/+page.server.ts b/src/routes/signup/+page.server.ts index 194da35..6cb10ca 100644 --- a/src/routes/signup/+page.server.ts +++ b/src/routes/signup/+page.server.ts @@ -1,22 +1,86 @@ import { db } from '$lib/server/db'; -import { userTable } from '$lib/server/db/schema'; +import { userTable, sessionTable } from '$lib/server/db/schema'; +import { z } from 'zod'; +import { fail, redirect } from '@sveltejs/kit'; import type { Actions } from './$types'; +import { randomBytes, scrypt } from 'crypto'; + +const userSchema = z.object({ + name: z.string().min(3, "Username must be at least 3 characters long").max(32, "Username must be at most 32 characters long"), + email: z.email("Invalid email address"), + password: z.string().min(8, "Password must be at least 8 characters long") +}); + +export const _hash = async (password: string, keyLength = 32): Promise<{ hash: string; salt: string }> => { + return new Promise((resolve, reject) => { + const salt = randomBytes(16).toString("hex"); + scrypt(password, salt, keyLength, (err, derivedKey) => { + if (err) reject(err); + resolve({ hash: derivedKey.toString("hex"), salt }); + }); + }); +}; export const actions = { - default: async ({ request }) => { - const formData = await request.formData(); - const name = formData.get('name'); - const email = formData.get('email'); - const password = formData.get('password'); + default: async ({ request, cookies }) => { + const formData = await request.formData(); + const name = formData.get('name')?.toString(); + const email = formData.get('email')?.toString(); + const password = formData.get('password')?.toString(); - // TODO: Implement data validation. + const parseResult = userSchema.safeParse({ name, email, password }); - const userRecord = await db.insert(userTable).values({ - name: name as string, - email: email as string, - password: password as string - }).returning(); + if (!parseResult.success) { + return fail(400, { + data: { name, email }, + errors: z.flattenError(parseResult.error).fieldErrors + }); + } - // TODO: Handle post-signup logic (e.g., redirect, session creation). - } + try { + const hashedpw = await _hash(parseResult.data.password); + + const newUser = await db.insert(userTable).values({ + name: parseResult.data.name, + email: parseResult.data.email, + password: hashedpw.hash, + salt: hashedpw.salt + }).returning(); + + const sessionToken = randomBytes(32).toString("hex"); + const expiresAt = new Date(); + expiresAt.setDate(expiresAt.getDate() + 7); + + await db.insert(sessionTable).values({ + user_id: newUser[0].id, + token: sessionToken, + expires_at: expiresAt + }); + + cookies.set('session', sessionToken, { + path: '/', + httpOnly: true, + sameSite: 'lax', + secure: process.env.NODE_ENV === 'production', + expires: expiresAt + }); + + } catch (error) { + if (error instanceof Error && 'code' in error && error.code === '23505') { + return fail(400, { + data: { name, email }, + errors: { + email: ["Email is already in use"] + } + }); + } + + console.error("Unexpected error during user registration:", error); + return fail(500, { + message: "An unexpected error occurred." + }); + } + + throw redirect(303, '/dashboard'); + } } satisfies Actions; \ No newline at end of file diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte index bb4e4a7..32ad1a9 100644 --- a/src/routes/signup/+page.svelte +++ b/src/routes/signup/+page.svelte @@ -1,6 +1,15 @@ + +
-
\ No newline at end of file +