118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
"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<PasswordActionState> {
|
|
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<PasswordActionState> {
|
|
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<PasswordActionState> {
|
|
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.",
|
|
),
|
|
};
|
|
}
|
|
}
|