feat(anmeldung): OIDC, Passkeys und Passwort mit Auth.js v5
Drei Wege hinein, je Instanz zuschaltbar: generischer OIDC-Anbieter (Entra ID beziehungsweise authentik), Passkeys und wahlweise E-Mail mit Passwort. Entscheidungen: - Passkeys laufen direkt über SimpleWebAuthn 14, nicht über den experimentellen WebAuthn-Provider von Auth.js, der noch auf Fassung 9 festgelegt ist. - Eigener Prisma-Adapter, weil der mitgelieferte fest auf prisma.user und prisma.account zugreift, unsere Modelle aber Benutzer und Konto heißen. - Sitzungen als JWT, weil Credentials-Provider das verlangen. Damit eine Sperre trotzdem sofort wirkt, liest der session-Callback Rolle und Status bei jeder Anfrage frisch aus der Datenbank. Die Tabellen sitzung und verifizierungs_token entfallen deshalb wieder. - allowDangerousEmailAccountLinking ist bewusst an: je Instanz gibt es genau einen vertrauenswürdigen Anbieter, der die Adresse selbst prüft. Anmeldung, Passwort und Passkey antworten bei Fehlschlag immer gleich, damit sich nicht abfragen lässt, wer hier ein Konto hat. Die WebAuthn-Herausforderung wird nach einem Versuch verworfen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4ff67c15c2
commit
cc013fd6a1
@@ -0,0 +1,147 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Rolle" AS ENUM ('ADMIN', 'BENUTZER');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "benutzer" (
|
||||
"id" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"name" TEXT,
|
||||
"passwortHash" TEXT,
|
||||
"rolle" "Rolle" NOT NULL DEFAULT 'BENUTZER',
|
||||
"aktiv" BOOLEAN NOT NULL DEFAULT true,
|
||||
"bildUrl" TEXT,
|
||||
"emailGeprueft" TIMESTAMP(3),
|
||||
"zuletztAktiv" TIMESTAMP(3),
|
||||
"erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"aktualisiertAm" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "benutzer_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "konto" (
|
||||
"id" TEXT NOT NULL,
|
||||
"benutzerId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
"refresh_token" TEXT,
|
||||
"access_token" TEXT,
|
||||
"expires_at" INTEGER,
|
||||
"token_type" TEXT,
|
||||
"scope" TEXT,
|
||||
"id_token" TEXT,
|
||||
"session_state" TEXT,
|
||||
|
||||
CONSTRAINT "konto_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "passkey" (
|
||||
"id" TEXT NOT NULL,
|
||||
"benutzerId" TEXT NOT NULL,
|
||||
"credentialId" TEXT NOT NULL,
|
||||
"publicKey" BYTEA NOT NULL,
|
||||
"counter" BIGINT NOT NULL DEFAULT 0,
|
||||
"transports" TEXT,
|
||||
"geraetetyp" TEXT,
|
||||
"gesichert" BOOLEAN NOT NULL DEFAULT false,
|
||||
"bezeichnung" TEXT NOT NULL,
|
||||
"erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"zuletztBenutzt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "passkey_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "webauthn_challenge" (
|
||||
"id" TEXT NOT NULL,
|
||||
"kennung" TEXT NOT NULL,
|
||||
"challenge" TEXT NOT NULL,
|
||||
"zweck" TEXT NOT NULL,
|
||||
"gueltigBis" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "webauthn_challenge_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "bereich" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"farbe" TEXT NOT NULL,
|
||||
"reihenfolge" INTEGER NOT NULL DEFAULT 0,
|
||||
"aktiv" BOOLEAN NOT NULL DEFAULT true,
|
||||
"kapazitaetMo" INTEGER NOT NULL DEFAULT 0,
|
||||
"kapazitaetDi" INTEGER NOT NULL DEFAULT 0,
|
||||
"kapazitaetMi" INTEGER NOT NULL DEFAULT 0,
|
||||
"kapazitaetDo" INTEGER NOT NULL DEFAULT 0,
|
||||
"kapazitaetFr" INTEGER NOT NULL DEFAULT 0,
|
||||
"kapazitaetSa" INTEGER NOT NULL DEFAULT 0,
|
||||
"kapazitaetSo" INTEGER NOT NULL DEFAULT 0,
|
||||
"arbeitsfensterVon" INTEGER NOT NULL DEFAULT 480,
|
||||
"arbeitsfensterBis" INTEGER NOT NULL DEFAULT 1020,
|
||||
"erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"aktualisiertAm" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "bereich_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "audit_eintrag" (
|
||||
"id" TEXT NOT NULL,
|
||||
"zeitpunkt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"benutzerId" TEXT,
|
||||
"benutzerEmail" TEXT NOT NULL,
|
||||
"aktion" TEXT NOT NULL,
|
||||
"objektTyp" TEXT NOT NULL,
|
||||
"objektId" TEXT,
|
||||
"details" JSONB,
|
||||
"instanz" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "audit_eintrag_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "benutzer_email_key" ON "benutzer"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "benutzer_aktiv_idx" ON "benutzer"("aktiv");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "konto_benutzerId_idx" ON "konto"("benutzerId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "konto_provider_providerAccountId_key" ON "konto"("provider", "providerAccountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "passkey_credentialId_key" ON "passkey"("credentialId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "passkey_benutzerId_idx" ON "passkey"("benutzerId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "webauthn_challenge_kennung_zweck_idx" ON "webauthn_challenge"("kennung", "zweck");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "bereich_name_key" ON "bereich"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "bereich_aktiv_reihenfolge_idx" ON "bereich"("aktiv", "reihenfolge");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_eintrag_zeitpunkt_idx" ON "audit_eintrag"("zeitpunkt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_eintrag_objektTyp_objektId_idx" ON "audit_eintrag"("objektTyp", "objektId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "audit_eintrag_benutzerId_idx" ON "audit_eintrag"("benutzerId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "konto" ADD CONSTRAINT "konto_benutzerId_fkey" FOREIGN KEY ("benutzerId") REFERENCES "benutzer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "passkey" ADD CONSTRAINT "passkey_benutzerId_fkey" FOREIGN KEY ("benutzerId") REFERENCES "benutzer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "audit_eintrag" ADD CONSTRAINT "audit_eintrag_benutzerId_fkey" FOREIGN KEY ("benutzerId") REFERENCES "benutzer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
Reference in New Issue
Block a user