From 72459d8da59040d160d2fca5def3d3af446d2c87 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 18 Jul 2026 00:46:40 +0200 Subject: [PATCH] Konto: Passwort-Anmeldung pro Nutzer deaktivierbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schalter im eigenen Konto, erzwingt vorher mindestens einen registrierten Passkey (sonst Aussperrung). Durchsetzung beim Login: nach erfolgreicher Passwort-Prüfung wird bei deaktiviertem Flag sofort wieder abgemeldet; Passkey-Anmeldung bleibt davon unberührt. --- app/konto/page.tsx | 3 +- app/login/page.tsx | 17 +++++++- components/AccountManager.tsx | 77 ++++++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/app/konto/page.tsx b/app/konto/page.tsx index 2256892..75fb402 100644 --- a/app/konto/page.tsx +++ b/app/konto/page.tsx @@ -12,7 +12,7 @@ export default async function KontoPage() { const { data: profile } = await supabase .from("profiles") - .select("role, full_name") + .select("role, full_name, password_auth_enabled") .eq("id", user.id) .single(); if (!profile || !["staff", "admin"].includes(profile.role)) redirect("/"); @@ -28,6 +28,7 @@ export default async function KontoPage() { role={profile.role} initialFullName={profile.full_name ?? ""} initialEmail={user.email ?? ""} + initialPasswordAuthEnabled={profile.password_auth_enabled} /> diff --git a/app/login/page.tsx b/app/login/page.tsx index 1c73697..2cb341f 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -46,7 +46,22 @@ export default function LoginPage() { setBusy(false); return; } - await redirectByRole(data.user.id); + + const { data: profile } = await supabase + .from("profiles") + .select("role, password_auth_enabled") + .eq("id", data.user.id) + .single(); + if (profile?.password_auth_enabled === false) { + await supabase.auth.signOut(); + setError( + "Die Anmeldung mit Passwort ist für dieses Konto deaktiviert. Bitte mit Passkey anmelden." + ); + setBusy(false); + return; + } + router.replace(profile?.role === "admin" ? "/admin" : "/intern"); + router.refresh(); } async function signInWithPasskey() { diff --git a/components/AccountManager.tsx b/components/AccountManager.tsx index 21b78fb..3a7dfb7 100644 --- a/components/AccountManager.tsx +++ b/components/AccountManager.tsx @@ -1,6 +1,6 @@ "use client"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { createClient } from "@/lib/supabase/client"; import PasskeyManager from "@/components/PasskeyManager"; @@ -24,10 +24,12 @@ export default function AccountManager({ role, initialFullName, initialEmail, + initialPasswordAuthEnabled, }: { role: string; initialFullName: string; initialEmail: string; + initialPasswordAuthEnabled: boolean; }) { const supabase = useMemo(() => createClient(), []); const router = useRouter(); @@ -52,6 +54,21 @@ export default function AccountManager({ const [pwInfo, setPwInfo] = useState(null); const [pwBusy, setPwBusy] = useState(false); + // Passwort-Anmeldung an/aus + const [passwordAuthEnabled, setPasswordAuthEnabled] = useState( + initialPasswordAuthEnabled + ); + const [passkeyCount, setPasskeyCount] = useState(null); + const [authError, setAuthError] = useState(null); + const [authInfo, setAuthInfo] = useState(null); + const [authBusy, setAuthBusy] = useState(false); + + useEffect(() => { + supabase.auth.passkey + .list() + .then(({ data }) => setPasskeyCount((data ?? []).length)); + }, [supabase]); + async function saveName(e: React.FormEvent) { e.preventDefault(); setNameError(null); @@ -129,6 +146,40 @@ export default function AccountManager({ setPwBusy(false); } + async function togglePasswordAuth() { + setAuthError(null); + setAuthInfo(null); + const next = !passwordAuthEnabled; + if (!next && (passkeyCount ?? 0) === 0) { + setAuthError( + "Ohne registrierten Passkey kannst du die Passwort-Anmeldung nicht deaktivieren – sonst wärst du ausgesperrt. Registriere zuerst einen Passkey weiter unten." + ); + return; + } + if ( + !next && + !window.confirm( + "Anmeldung mit E-Mail und Passwort wirklich deaktivieren? Du kannst dich danach nur noch per Passkey anmelden." + ) + ) + return; + + setAuthBusy(true); + const { error } = await supabase + .from("profiles") + .update({ password_auth_enabled: next }) + .eq("id", (await supabase.auth.getUser()).data.user?.id ?? ""); + if (error) { + setAuthError(`Speichern fehlgeschlagen: ${error.message}`); + } else { + setPasswordAuthEnabled(next); + setAuthInfo( + next ? "Passwort-Anmeldung aktiviert." : "Passwort-Anmeldung deaktiviert." + ); + } + setAuthBusy(false); + } + return (
@@ -256,6 +307,30 @@ export default function AccountManager({ +
+

Anmeldung mit Passwort

+

+ Du kannst die Anmeldung mit E-Mail und Passwort für dein Konto + deaktivieren und dich künftig ausschließlich per Passkey anmelden. + Dafür muss mindestens ein Passkey registriert sein. +

+ {authError && ( +

+ {authError} +

+ )} + {authInfo &&

{authInfo}

} + +
+
);