Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
43 lines
1.7 KiB
TypeScript
43 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import { useActionState } from "react";
|
|
import { confirmOwnMfaEnrollment, type MfaEnrollState } from "@/server/actions/account";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
const initial: MfaEnrollState = { status: "idle" };
|
|
|
|
/** MFA-Einrichtung für den angemeldeten Mandanten-Nutzer (QR wird serverseitig erzeugt). */
|
|
export function TenantMfaEnrollForm() {
|
|
const [state, action, pending] = useActionState(confirmOwnMfaEnrollment, initial);
|
|
|
|
if (state.status === "done") {
|
|
return (
|
|
<div className="space-y-3">
|
|
<div className="rounded-lg bg-[rgba(46,204,113,0.14)] px-3 py-2 text-sm text-[var(--ok)]">
|
|
MFA ist aktiv. Bewahren Sie diese Recovery-Codes sicher auf — sie werden <strong>nur jetzt</strong> angezeigt.
|
|
</div>
|
|
<ul className="grid grid-cols-2 gap-2 font-mono text-sm">
|
|
{state.recoveryCodes.map((c) => (
|
|
<li key={c} className="rounded border bg-card px-3 py-1.5 text-center tracking-wider">{c}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<form action={action} className="space-y-3">
|
|
<div>
|
|
<Label htmlFor="mfa-token">6-stelliger Code aus der Authenticator-App</Label>
|
|
<Input id="mfa-token" name="token" inputMode="numeric" autoComplete="one-time-code" placeholder="123456" required className="mt-1" />
|
|
</div>
|
|
{state.status === "error" && (
|
|
<p role="alert" className="rounded-lg bg-[rgba(255,107,107,0.16)] px-3 py-2 text-sm text-[var(--risk)]">{state.message}</p>
|
|
)}
|
|
<Button type="submit" size="sm" disabled={pending}>{pending ? "Prüfe…" : "MFA aktivieren"}</Button>
|
|
</form>
|
|
);
|
|
}
|