feat: auth endpoints (session-based)
This commit is contained in:
parent
0f85059e92
commit
2c48c8b55b
56
server/api/auth/login.post.ts
Normal file
56
server/api/auth/login.post.ts
Normal file
|
|
@ -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 };
|
||||
});
|
||||
11
server/api/auth/logout.post.ts
Normal file
11
server/api/auth/logout.post.ts
Normal file
|
|
@ -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.' };
|
||||
});
|
||||
38
server/api/auth/me.get.ts
Normal file
38
server/api/auth/me.get.ts
Normal file
|
|
@ -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 };
|
||||
});
|
||||
50
server/api/auth/register.post.ts
Normal file
50
server/api/auth/register.post.ts
Normal file
|
|
@ -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 };
|
||||
});
|
||||
13
server/utils/password.ts
Normal file
13
server/utils/password.ts
Normal file
|
|
@ -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<string> => {
|
||||
// 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<boolean> => {
|
||||
// In a real implementation, the hashing library would handle this comparison securely.
|
||||
return `${password}-hashed` === hash;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user