- lib/session.ts: token generatie, SHA-256 hashing, createSession/getCurrentUser/invalidateSession - app/api/auth/login: bcrypt verificatie, session aanmaken, ops_session cookie (httpOnly, sameSite=strict, 24h TTL), rate-limit 5/min per IP - app/api/auth/logout: session invalideren en cookie verwijderen - app/login/page.tsx: login form (client component) - proxy.ts: route-protectie – redirect naar /login zonder sessie (middleware.ts is deprecated in Next.js 16) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
52 lines
1.3 KiB
TypeScript
52 lines
1.3 KiB
TypeScript
import 'server-only'
|
|
import { cookies } from 'next/headers'
|
|
import { createHash, randomBytes } from 'crypto'
|
|
import { prisma } from './prisma'
|
|
|
|
const COOKIE_NAME = 'ops_session'
|
|
const SESSION_TTL_MS = 24 * 60 * 60 * 1000
|
|
|
|
export function generateSessionToken(): string {
|
|
return randomBytes(32).toString('hex')
|
|
}
|
|
|
|
export function hashToken(token: string): string {
|
|
return createHash('sha256').update(token).digest('hex')
|
|
}
|
|
|
|
export async function createSession(userId: string, token: string): Promise<void> {
|
|
const expiresAt = new Date(Date.now() + SESSION_TTL_MS)
|
|
await prisma.session.create({
|
|
data: {
|
|
user_id: userId,
|
|
token_hash: hashToken(token),
|
|
expires_at: expiresAt,
|
|
},
|
|
})
|
|
}
|
|
|
|
export async function getCurrentUser() {
|
|
const cookieStore = await cookies()
|
|
const token = cookieStore.get(COOKIE_NAME)?.value
|
|
if (!token) return null
|
|
|
|
const session = await prisma.session.findUnique({
|
|
where: { token_hash: hashToken(token) },
|
|
include: { user: true },
|
|
})
|
|
|
|
if (!session) return null
|
|
|
|
if (session.expires_at < new Date()) {
|
|
await prisma.session.delete({ where: { id: session.id } })
|
|
return null
|
|
}
|
|
|
|
return session.user
|
|
}
|
|
|
|
export async function invalidateSession(token: string): Promise<void> {
|
|
await prisma.session.deleteMany({
|
|
where: { token_hash: hashToken(token) },
|
|
})
|
|
}
|