feat(ST-l9kkxh2m): /reset-password pagina + resetPasswordAction + hashPassword

- lib/auth.ts: hashPassword(password) geëxporteerd (bcrypt, rounds=12)
- actions/auth.ts: resetPasswordAction met Zod-validatie (min 8, superRefine gelijkheid),
  prisma.user.update (password_hash + must_reset_password=false), redirect /dashboard
- app/(auth)/reset-password/page.tsx: server guard (userId check + must_reset_password check)
- app/(auth)/reset-password/reset-form.tsx: client form (nieuw wachtwoord + bevestiging)
- __tests__/actions/auth.test.ts: 3 tests voor resetPasswordAction
This commit is contained in:
Scrum4Me Agent 2026-05-05 14:30:59 +02:00
parent 71281038ff
commit a19ae89e37
5 changed files with 206 additions and 3 deletions

View file

@ -4,10 +4,12 @@ import { redirect } from 'next/navigation'
import { cookies, headers } from 'next/headers'
import { getIronSession } from 'iron-session'
import { z } from 'zod'
import { registerUser, verifyUser } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { registerUser, verifyUser, hashPassword } from '@/lib/auth'
import { SessionData, sessionOptions } from '@/lib/session'
import { checkRateLimit } from '@/lib/rate-limit'
import { isPhoneUA } from '@/lib/user-agent'
import { requireSession } from '@/lib/auth-guard'
async function getClientIp(): Promise<string> {
const h = await headers()
@ -90,3 +92,39 @@ export async function logoutAction() {
session.destroy()
redirect('/login')
}
const resetPasswordSchema = z
.object({
password: z.string().min(8, 'Wachtwoord moet minimaal 8 tekens bevatten'),
confirm: z.string(),
})
.superRefine((data, ctx) => {
if (data.password !== data.confirm) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Wachtwoorden komen niet overeen',
path: ['confirm'],
})
}
})
export async function resetPasswordAction(_prevState: unknown, formData: FormData) {
const session = await requireSession()
const parsed = resetPasswordSchema.safeParse({
password: formData.get('password'),
confirm: formData.get('confirm'),
})
if (!parsed.success) {
return { error: parsed.error.flatten().fieldErrors }
}
const hash = await hashPassword(parsed.data.password)
await prisma.user.update({
where: { id: session.userId },
data: { password_hash: hash, must_reset_password: false },
})
redirect('/dashboard')
}