diff --git a/server/api/auth/login.post.ts b/server/api/auth/login.post.ts new file mode 100644 index 0000000..817fbf2 --- /dev/null +++ b/server/api/auth/login.post.ts @@ -0,0 +1,56 @@ +import { PrismaClient } from '@prisma/client'; +import { verifyPassword } from '../utils/password'; +import { useSession } from 'h3'; + +const prisma = new PrismaClient(); + +export default defineEventHandler(async (event) => { + const body = await readBody(event); + const { email, password } = body; + + // 1. Validate input + if (!email || !password) { + throw createError({ + statusCode: 400, + statusMessage: 'Email and password are required', + }); + } + + // 2. Find the user + const user = await prisma.user.findUnique({ + where: { email }, + }); + + if (!user) { + throw createError({ + statusCode: 401, // Unauthorized + statusMessage: 'Invalid credentials', + }); + } + + // 3. Verify the password + const isPasswordValid = await verifyPassword(password, user.password); + if (!isPasswordValid) { + throw createError({ + statusCode: 401, + statusMessage: 'Invalid credentials', + }); + } + + // 4. Create and update the session + const session = await useSession(event, { + password: process.env.SESSION_PASSWORD || 'your-super-secret-32-character-password', // Should be in .env + maxAge: 60 * 60 * 24 * 7, // 1 week + }); + + await session.update({ + user: { + id: user.id, + email: user.email, + } + }); + + // 5. Return user data + const { password: _password, ...userWithoutPassword } = user; + return { user: userWithoutPassword }; +}); diff --git a/server/api/auth/logout.post.ts b/server/api/auth/logout.post.ts new file mode 100644 index 0000000..3498ba9 --- /dev/null +++ b/server/api/auth/logout.post.ts @@ -0,0 +1,11 @@ +import { useSession } from 'h3'; + +export default defineEventHandler(async (event) => { + const session = await useSession(event, { + password: process.env.SESSION_PASSWORD || 'your-super-secret-32-character-password', + }); + + await session.clear(); + + return { message: 'Logged out successfully.' }; +}); diff --git a/server/api/auth/me.get.ts b/server/api/auth/me.get.ts new file mode 100644 index 0000000..9e0de6c --- /dev/null +++ b/server/api/auth/me.get.ts @@ -0,0 +1,38 @@ +import { PrismaClient } from '@prisma/client'; +import { useSession } from 'h3'; + +const prisma = new PrismaClient(); + +export default defineEventHandler(async (event) => { + // 1. Get the session + const session = await useSession(event, { + password: process.env.SESSION_PASSWORD || 'your-super-secret-32-character-password', + }); + + // 2. Check if user is in session + if (!session.data?.user?.id) { + throw createError({ + statusCode: 401, + statusMessage: 'Unauthorized', + }); + } + + // 3. Fetch the full user from the database + const user = await prisma.user.findUnique({ + where: { id: session.data.user.id }, + }); + + if (!user) { + // This case might happen if the user was deleted but the session still exists. + // Clear the invalid session. + await session.clear(); + throw createError({ + statusCode: 401, + statusMessage: 'Unauthorized', + }); + } + + // 4. Return user data + const { password: _password, ...userWithoutPassword } = user; + return { user: userWithoutPassword }; +}); diff --git a/server/api/auth/register.post.ts b/server/api/auth/register.post.ts new file mode 100644 index 0000000..525da4d --- /dev/null +++ b/server/api/auth/register.post.ts @@ -0,0 +1,50 @@ +import { PrismaClient } from '@prisma/client'; +import { hashPassword } from '../utils/password'; + +const prisma = new PrismaClient(); + +export default defineEventHandler(async (event) => { + const body = await readBody(event); + const { email, password, nickname } = body; + + // 1. Validate input + if (!email || !password) { + throw createError({ + statusCode: 400, + statusMessage: 'Email and password are required', + }); + } + if (password.length < 8) { + throw createError({ + statusCode: 400, + statusMessage: 'Password must be at least 8 characters long', + }); + } + + + // 2. Check if user already exists + const existingUser = await prisma.user.findUnique({ + where: { email }, + }); + + if (existingUser) { + throw createError({ + statusCode: 409, // Conflict + statusMessage: 'Email already in use', + }); + } + + // 3. Hash password and create user + const hashedPassword = await hashPassword(password); + const user = await prisma.user.create({ + data: { + email, + password: hashedPassword, + nickname: nickname || 'New Smurf', + }, + }); + + // 4. Return the new user, excluding the password + const { password: _password, ...userWithoutPassword } = user; + return { user: userWithoutPassword }; +}); diff --git a/server/utils/password.ts b/server/utils/password.ts new file mode 100644 index 0000000..585ea1e --- /dev/null +++ b/server/utils/password.ts @@ -0,0 +1,13 @@ +// WARNING: This is a placeholder for demonstration purposes only. +// It is NOT secure and should be replaced with a proper password hashing +// library like bcrypt or argon2 before any real-world use. + +export const hashPassword = async (password: string): Promise => { + // In a real implementation, you would use a salt and a strong hashing algorithm. + return `${password}-hashed`; +}; + +export const verifyPassword = async (password: string, hash: string): Promise => { + // In a real implementation, the hashing library would handle this comparison securely. + return `${password}-hashed` === hash; +};