Files
kita-planer/src/app/login/actions.ts
T

68 lines
2.0 KiB
TypeScript

"use server";
import { AuthError } from "next-auth";
import { z } from "zod";
import { signIn } from "@/auth";
// =====================================================================
// /login · Server Action
// ---------------------------------------------------------------------
// Wir kapseln `signIn("credentials", …)` damit:
// • das Formular `useActionState` mit typisiertem Fehler-State nutzen kann
// • der Redirect-Trigger (NEXT_REDIRECT) sauber durchgereicht wird
// • generische Fehler ("Login fehlgeschlagen") weder Username-Existenz
// noch Lockout-Status leaken (kein User-Enumeration).
// =====================================================================
const loginSchema = z.object({
email: z.string().email("Bitte eine gültige E-Mail-Adresse angeben.").toLowerCase().trim(),
password: z.string().min(1, "Passwort fehlt."),
callbackUrl: z.string().optional(),
});
export type LoginState = {
errors?: {
email?: string[];
password?: string[];
_form?: string[];
};
};
function safeCallbackUrl(value: string | undefined) {
if (!value) return "/dashboard";
if (!value.startsWith("/") || value.startsWith("//")) return "/dashboard";
return value;
}
export async function loginAction(
_prev: LoginState,
formData: FormData,
): Promise<LoginState> {
const parsed = loginSchema.safeParse(Object.fromEntries(formData));
if (!parsed.success) {
return { errors: parsed.error.flatten().fieldErrors };
}
const { email, password } = parsed.data;
const redirectTo = safeCallbackUrl(parsed.data.callbackUrl);
try {
await signIn("credentials", {
email,
password,
redirectTo,
});
} catch (err) {
// signIn wirft NEXT_REDIRECT bei Erfolg → das müssen wir bewusst
// weiterwerfen, sonst wird der Redirect verschluckt.
if (err instanceof AuthError) {
return {
errors: { _form: ["E-Mail oder Passwort ist nicht korrekt."] },
};
}
throw err;
}
return {};
}