Ops-dashboard/app/api/auth/login/route.ts
Scrum4Me Agent be05724de0 feat: login page, session management, auth API routes en proxy guard
- 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>
2026-05-13 17:10:07 +02:00

62 lines
1.8 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import bcrypt from 'bcryptjs'
import { prisma } from '@/lib/prisma'
import { generateSessionToken, createSession } from '@/lib/session'
const loginAttempts = new Map<string, number[]>()
const MAX_ATTEMPTS = 5
const WINDOW_MS = 60_000
function isRateLimited(ip: string): boolean {
const now = Date.now()
const attempts = (loginAttempts.get(ip) ?? []).filter((t) => now - t < WINDOW_MS)
attempts.push(now)
loginAttempts.set(ip, attempts)
return attempts.length > MAX_ATTEMPTS
}
export async function POST(request: NextRequest) {
const ip =
request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown'
if (isRateLimited(ip)) {
return NextResponse.json({ error: 'Too many requests' }, { status: 429 })
}
let email: string, password: string
try {
const body = await request.json()
email = (body.email ?? '').trim()
password = body.password ?? ''
} catch {
return NextResponse.json({ error: 'Invalid request body' }, { status: 400 })
}
if (!email || !password) {
return NextResponse.json(
{ error: 'Email and password are required' },
{ status: 400 }
)
}
const user = await prisma.user.findUnique({ where: { email } })
const validPassword =
user != null && (await bcrypt.compare(password, user.pwd_hash))
if (!user || !validPassword) {
return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 })
}
const token = generateSessionToken()
await createSession(user.id, token)
const response = NextResponse.json({ success: true })
response.cookies.set('ops_session', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 60 * 60 * 24,
path: '/',
})
return response
}