feat(auth): Anmeldung mit Passwort und OIDC, Schutz aller Seiten (#013)

Signierte Sitzungs-Cookies mit jose, OIDC ueber openid-client (authentik),
Schutz in proxy.ts, Anmeldeseite, Audit-Log-Ansicht, Sicherheits-Kopfzeilen und
Rate-Limit. Kein Auth.js, weil Version 5 Beta ist. 147 Tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 22:40:03 +02:00
co-authored by Claude Opus 5
parent 6cc39591b7
commit ee487775ec
26 changed files with 945 additions and 27 deletions
+38
View File
@@ -0,0 +1,38 @@
import { NextResponse, type NextRequest } from "next/server";
import { SITZUNGS_COOKIE, sitzungLesen } from "@/lib/sitzung";
/**
* Schützt die Oberfläche: Ohne gültige Sitzung führt jeder Weg zur Anmeldung.
* Die Agent-API bleibt bewusst außen vor – sie authentifiziert sich mit eigenen Tokens.
* (In Next 16 heißt diese Datei `proxy.ts`, früher `middleware.ts`.)
*/
const OEFFENTLICH = [
"/login",
"/api/auth",
"/api/agent",
"/install.sh",
"/agent.sh",
"/favicon.ico",
"/icon.svg",
];
export async function proxy(request: NextRequest) {
const pfad = request.nextUrl.pathname;
if (OEFFENTLICH.some((eintrag) => pfad === eintrag || pfad.startsWith(`${eintrag}/`))) {
return NextResponse.next();
}
const sitzung = await sitzungLesen(request.cookies.get(SITZUNGS_COOKIE)?.value);
if (sitzung) return NextResponse.next();
const ziel = new URL("/login", request.url);
if (pfad !== "/") ziel.searchParams.set("ziel", `${pfad}${request.nextUrl.search}`);
return NextResponse.redirect(ziel);
}
export const config = {
// Statische Dateien und Bilder brauchen keine Prüfung.
matcher: ["/((?!_next/static|_next/image|.*\\.(?:png|jpg|svg|ico|woff2)$).*)"],
};