feat(mvp): phase 3 - authentication system
Implement local auth flow: login, registration, logout, JWT access/refresh tokens in HTTP-only cookies, hooks.server.ts middleware, guest mode support, Superforms + Zod validation, and reusable auth/authorize middleware.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
|
||||
/**
|
||||
* Reusable authentication check helper.
|
||||
* Throws a redirect to /login if the user is not authenticated.
|
||||
* Returns the authenticated user from event.locals.
|
||||
*/
|
||||
export function requireAuth(event: RequestEvent) {
|
||||
const user = event.locals.user;
|
||||
if (!user) {
|
||||
throw redirect(302, '/login');
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request has an authenticated user without redirecting.
|
||||
*/
|
||||
export function isAuthenticated(event: RequestEvent): boolean {
|
||||
return event.locals.user !== null;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
import { requireAuth } from './authenticate.js';
|
||||
import { UserRole } from '$lib/utils/constants.js';
|
||||
|
||||
/**
|
||||
* Role-based access check. Ensures the user is authenticated and has one of the required roles.
|
||||
* Throws a 403 error if the user's role is not in the allowed list.
|
||||
*/
|
||||
export function requireRole(event: RequestEvent, ...allowedRoles: string[]) {
|
||||
const user = requireAuth(event);
|
||||
|
||||
if (!allowedRoles.includes(user.role)) {
|
||||
throw error(403, { message: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand: require admin role.
|
||||
*/
|
||||
export function requireAdmin(event: RequestEvent) {
|
||||
return requireRole(event, UserRole.ADMIN);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { prisma } from '../prisma.js';
|
||||
|
||||
/**
|
||||
* Check if a board is guest-accessible (visible to unauthenticated users).
|
||||
*/
|
||||
export async function isBoardGuestAccessible(boardId: string): Promise<boolean> {
|
||||
const board = await prisma.board.findUnique({
|
||||
where: { id: boardId },
|
||||
select: { isGuestAccessible: true }
|
||||
});
|
||||
return board?.isGuestAccessible ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all guest-accessible boards.
|
||||
*/
|
||||
export async function getGuestAccessibleBoards() {
|
||||
return prisma.board.findMany({
|
||||
where: { isGuestAccessible: true },
|
||||
orderBy: { name: 'asc' },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
icon: true,
|
||||
description: true,
|
||||
isDefault: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default guest-accessible board (if any).
|
||||
* Returns the first board that is both default and guest-accessible,
|
||||
* or the first guest-accessible board if none is default.
|
||||
*/
|
||||
export async function getDefaultGuestBoard() {
|
||||
const defaultBoard = await prisma.board.findFirst({
|
||||
where: { isGuestAccessible: true, isDefault: true },
|
||||
select: { id: true, name: true }
|
||||
});
|
||||
|
||||
if (defaultBoard) return defaultBoard;
|
||||
|
||||
return prisma.board.findFirst({
|
||||
where: { isGuestAccessible: true },
|
||||
orderBy: { name: 'asc' },
|
||||
select: { id: true, name: true }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* JWT utilities — thin re-exports from authService.
|
||||
* authService already handles sign, verify, and refresh token generation.
|
||||
*/
|
||||
export {
|
||||
signAccessToken,
|
||||
verifyAccessToken,
|
||||
generateRefreshToken,
|
||||
getRefreshTokenExpiry
|
||||
} from '../services/authService.js';
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Password utilities — thin re-exports from authService.
|
||||
* authService already handles bcrypt hash and compare.
|
||||
*/
|
||||
export { hashPassword, verifyPassword } from '../services/authService.js';
|
||||
Reference in New Issue
Block a user