Zwei-Faktor-Authentifizierung (optional)
+MFA ist aktiv. Verbleibende Recovery-Codes: {recoveryLeft}.
+ ++ Scannen Sie den QR-Code mit einer Authenticator-App und bestätigen Sie mit dem angezeigten Code. +
+{secret}
+ diff --git a/messages/de.json b/messages/de.json
index d05bc8b..bbd6239 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -41,6 +41,7 @@
"password": "Passwort",
"tenant": "Organisation (optional)",
"tenantHint": "Nur nötig, wenn deine E-Mail in mehreren Organisationen existiert.",
+ "mfaOptional": "MFA-Code (falls eingerichtet)",
"submit": "Anmelden",
"error": "Anmeldung fehlgeschlagen. Bitte prüfe E-Mail, Passwort und ggf. die Organisation."
},
diff --git a/messages/en.json b/messages/en.json
index ef6834f..f4f0963 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -41,6 +41,7 @@
"password": "Password",
"tenant": "Organization (optional)",
"tenantHint": "Only needed if your e-mail exists in several organizations.",
+ "mfaOptional": "MFA code (if enabled)",
"submit": "Sign in",
"error": "Sign-in failed. Please check e-mail, password and organization."
},
diff --git a/src/app/(app)/account/page.tsx b/src/app/(app)/account/page.tsx
new file mode 100644
index 0000000..024a14d
--- /dev/null
+++ b/src/app/(app)/account/page.tsx
@@ -0,0 +1,67 @@
+import { redirect } from "next/navigation";
+import QRCode from "qrcode";
+import { requireSession } from "@/server/auth";
+import { dbForTenant } from "@/server/db";
+import { newTotpSecret, totpUri } from "@/server/mfa";
+import { disableOwnMfa } from "@/server/actions/account";
+import { Button } from "@/components/ui/button";
+import { PageHead, Pill } from "@/components/mockup-ui";
+import { TenantMfaEnrollForm } from "@/components/tenant-mfa-enroll-form";
+
+/**
+ * Persönliches Konto des Mandanten-Nutzers (Paket C): optionale MFA selbst
+ * aktivieren/deaktivieren. Login verlangt den Code erst, sobald MFA aktiv ist.
+ */
+export default async function AccountPage() {
+ const session = await requireSession();
+ const db = dbForTenant(session.user.tenantId);
+ const user = await db.user.findUnique({ where: { id: session.user.id } });
+ if (!user) redirect("/login");
+
+ const enrolled = !!user.mfaEnrolledAt;
+ const recoveryLeft = Array.isArray(user.recoveryCodes) ? user.recoveryCodes.length : 0;
+
+ let qr: string | null = null;
+ let secret: string | null = null;
+ if (!enrolled) {
+ secret = user.mfaSecret ?? newTotpSecret();
+ if (!user.mfaSecret) await db.user.update({ where: { id: user.id }, data: { mfaSecret: secret } });
+ qr = await QRCode.toDataURL(totpUri(user.email, secret), { margin: 1, width: 192 });
+ }
+
+ return (
+ Zwei-Faktor-Authentifizierung (optional) MFA ist aktiv. Verbleibende Recovery-Codes: {recoveryLeft}.
+ Scannen Sie den QR-Code mit einer Authenticator-App und bestätigen Sie mit dem angezeigten Code.
+
+
{secret}
+
{session.user.name}
-{session.user.tenantSlug}
-{session.user.name}
+{session.user.tenantSlug}
+