Im Betrieb aufgetreten: Die Anwendung lief hinter einem Reverse Proxy unter einer Domain, in der .env stand aber noch die interne Adresse aus der Einrichtung. Der Anmeldeknopf setzt den Cookie mit dem laufenden Vorgang auf dem Host, ueber den der Browser da ist — die Rueckkehradresse fuer den Anbieter kommt dagegen aus BASE_URL. Der Anbieter schickte den Browser also auf den anderen Host, der Cookie blieb zurueck, und am Ende stand „Der Anmeldevorgang ist abgelaufen". Eine Meldung, die die Ursache gut versteckt. Jetzt prueft der Start beide Adressen und bricht sofort mit einer Erklaerung ab. Auf dem Bildschirm steht nur die aufgerufene Adresse — die interne gehoert ins Protokoll, nicht auf eine Seite, die jeder sieht. Aus demselben Grund faellt der Hinweis „Die Anmeldung mit Passwort ist abgeschaltet (OIDC_ONLY=true)" weg. Wie die Anmeldung eingerichtet ist, geht Fremde nichts an; wer hierher gehoert, sieht den einen Knopf und weiss, was zu tun ist. Beim allerersten Aufruf bleibt der Hinweis, dass der Erste Administrator wird — der ist dann nuetzlich. Dazu die Anleitungen: Der Reverse-Proxy-Abschnitt im README nennt jetzt die drei Kopfzeilen, die die Anwendung vom Proxy erwartet, und was ohne sie passiert; dazu client_max_body_size fuer Uploads, ein Weg zum Nachpruefen und die Frage, fuer wen die Codes erreichbar sein sollen. In authentik.md steht, warum BASE_URL und die Adresse im Browser dieselbe sein muessen.
116 lines
4.4 KiB
TypeScript
116 lines
4.4 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { redirect } from "next/navigation";
|
|
|
|
import { LoginForm, SetupForm } from "@/components/auth-forms";
|
|
import { Icon } from "@/components/icons";
|
|
import { Notice } from "@/components/ui";
|
|
import { getActor, isAuthEnabled, isSetupOpen } from "@/lib/auth/actor";
|
|
import { getOidcConfig, isOidcEnabled } from "@/lib/auth/oidc";
|
|
import { MIN_PASSWORD_LENGTH } from "@/lib/auth/password";
|
|
|
|
export const metadata: Metadata = { title: "Anmelden" };
|
|
export const dynamic = "force-dynamic";
|
|
|
|
type PageProps = { searchParams: Promise<{ fehler?: string }> };
|
|
|
|
export default async function SignInPage({ searchParams }: PageProps) {
|
|
// Ohne Anmeldung gibt es hier nichts zu tun.
|
|
if (!isAuthEnabled()) redirect("/");
|
|
|
|
const actor = await getActor();
|
|
if (actor.kind === "user") redirect("/");
|
|
|
|
const setup = await isSetupOpen();
|
|
const { fehler } = await searchParams;
|
|
|
|
// Eine unvollständige OIDC-Konfiguration soll die Anmeldeseite nicht
|
|
// unbenutzbar machen — dann bleibt wenigstens das Passwortfeld.
|
|
let oidc: { label: string; nurOidc: boolean } | null = null;
|
|
let oidcFehler: string | null = null;
|
|
if (isOidcEnabled()) {
|
|
try {
|
|
const config = getOidcConfig();
|
|
oidc = { label: config.buttonLabel, nurOidc: !config.allowLocalLogin };
|
|
} catch (error) {
|
|
oidcFehler =
|
|
error instanceof Error
|
|
? error.message
|
|
: "Die Anmeldung über den externen Anbieter ist nicht eingerichtet.";
|
|
}
|
|
}
|
|
|
|
// Bei reiner Anbieter-Anmeldung gibt es keine Ersteinrichtung mit Passwort:
|
|
// Der erste, der sich über den Anbieter anmeldet, wird Administrator.
|
|
const zeigeFormular = !oidc?.nurOidc;
|
|
|
|
return (
|
|
<div className="mx-auto flex max-w-md flex-col gap-6 px-4 py-14 sm:px-6">
|
|
<div className="text-center">
|
|
<span className="mx-auto flex size-12 items-center justify-center rounded-xl bg-indigo-600 text-white">
|
|
<Icon name={setup && zeigeFormular ? "lock" : "qr"} className="size-6" />
|
|
</span>
|
|
<h1 className="mt-4 text-2xl font-bold tracking-tight text-slate-900">
|
|
{setup && zeigeFormular ? "Ersteinrichtung" : "Anmelden"}
|
|
</h1>
|
|
<p className="mt-2 text-sm text-slate-600">
|
|
{setup && zeigeFormular
|
|
? "Es gibt noch keinen Benutzer. Lege jetzt den ersten Administrator an — danach ist diese Seite geschlossen."
|
|
: "Bitte melde dich an, um QR-Codes zu erstellen und zu verwalten."}
|
|
</p>
|
|
</div>
|
|
|
|
{fehler ? <Notice tone="warning">{fehler}</Notice> : null}
|
|
{oidcFehler ? <Notice tone="warning">{oidcFehler}</Notice> : null}
|
|
|
|
{oidc ? (
|
|
<a
|
|
href="/api/auth/oidc/start"
|
|
className="flex items-center justify-center gap-2 rounded-lg bg-indigo-600 px-4 py-3 font-medium text-white transition-colors hover:bg-indigo-700"
|
|
>
|
|
<Icon name="lock" className="size-4" />
|
|
{oidc.label}
|
|
</a>
|
|
) : null}
|
|
|
|
{oidc && zeigeFormular ? (
|
|
<div className="flex items-center gap-3 text-xs text-slate-400 uppercase">
|
|
<span className="h-px flex-1 bg-slate-200" />
|
|
oder mit Passwort
|
|
<span className="h-px flex-1 bg-slate-200" />
|
|
</div>
|
|
) : null}
|
|
|
|
{zeigeFormular ? (
|
|
setup ? (
|
|
<SetupForm minLength={MIN_PASSWORD_LENGTH} />
|
|
) : (
|
|
<LoginForm />
|
|
)
|
|
) : null}
|
|
|
|
{setup && zeigeFormular ? (
|
|
<Notice tone="info">
|
|
Der erste Benutzer wird Administrator der Installation und darf
|
|
Gruppen, Benutzer und die QR-Typen verwalten.
|
|
</Notice>
|
|
) : oidc?.nurOidc ? (
|
|
/* Bewusst kein Hinweis darauf, dass die Passwort-Anmeldung
|
|
abgeschaltet ist. Diese Seite sieht jeder, der die Adresse kennt —
|
|
wie die Anmeldung eingerichtet ist, geht Fremde nichts an. Wer
|
|
hierher gehört, sieht den einen Knopf und weiß, was zu tun ist.
|
|
Beim allerersten Aufruf ist der Hinweis dagegen nützlich. */
|
|
setup ? (
|
|
<p className="text-center text-sm text-slate-500">
|
|
Wer sich zuerst anmeldet, wird Administrator dieser Installation.
|
|
</p>
|
|
) : null
|
|
) : (
|
|
<p className="text-center text-sm text-slate-500">
|
|
Passwort vergessen? Ein Administrator kann es unter Verwaltung →
|
|
Benutzer zurücksetzen.
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|