Files
FPT/src/app/api/auth/change-password/route.ts
T
admGitAICDS b47d3d0a3e
Deploy App / deploy (push) Successful in 1m10s
Modul-Review 1: Auth-Nachbesserungen (Sicherheit & UX)
Ergebnis der ersten Test- und Review-Runde zum Modul "Zugang & App-Rahmen":

- Login-Timing-Ausgleich: unbekannter Benutzer wird gegen Dummy-bcrypt-Hash
  geprueft -> Antwortzeit verraet nicht mehr, ob ein Name existiert
- Zurueck-Knopf nach Logout: pageshow-Waechter prueft die Session erneut und
  leitet die aus dem bfcache zurueckgeholte Ansicht auf /login
- Rate-Limiting (neues lib/rate-limit.ts): Login 10/15min, Registrierung
  5/h je IP, Passwortaenderung 10/15min je Benutzer; 429 + Retry-After
- Passwort-Dialog laeuft neu ueber Modal -> schliesst auf Esc (Fokus-Falle,
  aria-modal inklusive)
- Registrierungs-Fehler getrennt: nur belegter Name = 409 mit freundlicher
  Meldung, sonst 500 statt roher Prisma-Meldung

SPEZIFIKATION 0.27 (3.1.2/3/4, neues 3.1.6). 261 -> 267 Tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 20:45:31 +02:00

41 lines
1.5 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { getCurrentUserId } from "@/lib/session";
import { changeUserPassword } from "@/lib/users";
import { rateLimit, tooManyRequests } from "@/lib/rate-limit";
const changePasswordSchema = z.object({
currentPassword: z.string().min(1),
newPassword: z.string().min(6, "Das neue Passwort muss mindestens 6 Zeichen lang sein."),
});
export async function POST(request: NextRequest) {
const userId = await getCurrentUserId();
if (!userId) {
return NextResponse.json({ error: "Nicht authentifiziert." }, { status: 401 });
}
// Bremse gegen das Durchprobieren des aktuellen Passworts: 10 Versuche je Benutzer und
// 15 Minuten (nach dem Auth-Check, damit der Schlüssel die echte Benutzer-ID ist).
const limited = rateLimit(`change-pw:${userId}`, 10, 15 * 60 * 1000);
if (!limited.ok) return tooManyRequests(limited.retryAfterSeconds);
const body = await request.json();
const parsed = changePasswordSchema.safeParse(body);
if (!parsed.success) {
const message = parsed.error.issues[0]?.message ?? "Ungültige Eingabe.";
return NextResponse.json({ error: message }, { status: 400 });
}
try {
await changeUserPassword(userId, parsed.data.currentPassword, parsed.data.newPassword);
} catch (e) {
return NextResponse.json(
{ error: e instanceof Error ? e.message : "Passwort ändern fehlgeschlagen." },
{ status: 400 }
);
}
return NextResponse.json({ ok: true });
}