feat: implement auth system, passkeys, and user management

Add PostgreSQL and Redis integration for users and sessions
Implement password and WebAuthn passkey login flows
Add Docker stack, super-admin seeding, and protected routes
This commit is contained in:
2026-04-12 20:16:43 +08:00
parent a649c509c2
commit 377a9617be
45 changed files with 3620 additions and 104 deletions

78
server/utils/auth.ts Normal file
View File

@@ -0,0 +1,78 @@
import type { H3Event } from 'h3'
import type { UserRole } from '~~/shared/auth'
import { createUserSession, destroyUserSession, getUserSession } from './session'
import { getUserById, updateLastLogin, type UserAuthRecord } from './user-repository'
export function normalizeUsername(value: string) {
return value.trim().toLowerCase()
}
export async function getAuthContext(event: H3Event): Promise<{
session: Awaited<ReturnType<typeof getUserSession>>
user: UserAuthRecord
} | null> {
const session = await getUserSession(event)
if (!session) {
return null
}
const user = await getUserById(session.userId)
if (!user || !user.isActive) {
await destroyUserSession(event)
return null
}
return {
session,
user
}
}
export async function requireAuth(event: H3Event) {
const auth = await getAuthContext(event)
if (!auth) {
throw createError({
statusCode: 401,
statusMessage: 'Authentication required'
})
}
return auth
}
export async function requireRole(event: H3Event, role: UserRole) {
const auth = await requireAuth(event)
if (auth.user.role !== role) {
throw createError({
statusCode: 403,
statusMessage: 'You are not allowed to perform this action'
})
}
return auth
}
export async function signInUser(event: H3Event, user: UserAuthRecord, remember: boolean) {
await createUserSession(event, {
userId: user.id,
remember
})
await updateLastLogin(user.id)
const refreshedUser = await getUserById(user.id)
if (!refreshedUser) {
throw createError({
statusCode: 500,
statusMessage: 'Unable to load authenticated user'
})
}
return refreshedUser
}