"use server"; import { z } from "zod"; import { after } from "next/server"; import { limitPublicAction } from "@/lib/rate-limit"; import { requestPasswordReset, passwordResetMessage, resetPassword, changeOwnPassword, } from "@/lib/password-management"; import { hashToken } from "@/lib/tokens"; import { requireSession } from "@/lib/auth-utils"; import { publicActionError } from "@/lib/action-error"; export type PasswordActionState = { error?: string; message?: string; success?: boolean; }; export async function requestResetAction( _previous: PasswordActionState, form: FormData, ): Promise { const email = z .string() .trim() .max(254) .email() .toLowerCase() .safeParse(form.get("email")); if (!email.success) return { error: "Bitte gib eine gültige E-Mail-Adresse ein." }; try { if (await limitPublicAction("password-reset-request", email.data, 3, 3600)) after(async () => { try { await requestPasswordReset(email.data); } catch { console.error( "Passwort-Wiederherstellung konnte nicht vorbereitet werden.", ); } }); } catch { console.error( "Passwort-Wiederherstellung konnte nicht vorbereitet werden.", ); } return { success: true, message: passwordResetMessage }; } export async function finishResetAction( _previous: PasswordActionState, form: FormData, ): Promise { const token = form.get("token"); if (typeof token !== "string" || !/^[a-f0-9]{64}$/i.test(token)) return { error: "Dieser Link ist ungültig. Bitte fordere einen neuen Link an.", }; try { if ( !(await limitPublicAction( "password-reset-complete", hashToken(token), 5, 900, )) ) return { error: "Zu viele Versuche. Bitte versuche es später erneut." }; await resetPassword({ token, password: form.get("password"), confirmation: form.get("confirmation"), }); return { success: true, message: "Dein Passwort wurde geändert. Melde dich mit deinem neuen Passwort an.", }; } catch (error) { return { error: publicActionError( error, "Das Passwort konnte nicht geändert werden. Bitte versuche es erneut.", ), }; } } export async function changePasswordAction( _previous: PasswordActionState, form: FormData, ): Promise { const { user } = await requireSession(); try { if (!(await limitPublicAction("password-change", user.id, 5, 900))) return { error: "Zu viele Versuche. Bitte versuche es später erneut." }; await changeOwnPassword( { id: user.id, sessionVersion: user.sessionVersion }, { currentPassword: form.get("currentPassword"), password: form.get("password"), confirmation: form.get("confirmation"), }, ); return { success: true, message: "Dein Passwort wurde geändert. Bitte melde dich erneut an.", }; } catch (error) { return { error: publicActionError( error, "Das Passwort konnte nicht geändert werden. Bitte versuche es erneut.", ), }; } }