d3718da1f8
Passkey-Login analog zu werte-next: auf der Login-Seite "Mit Passkey anmelden", Verwaltung der eigenen Passkeys im neuen Tab Einstellungen. Das Passwort bleibt als Alternative bestehen. - lib/webauthn.ts, lib/passkeys.ts sowie API-Routen unter /api/passkey - proxy.ts: /api/passkey/authenticate ohne Session erreichbar - TabLayout: dritter Tab Einstellungen (Index-Tabs statt Route wie werte) - RP-Konfiguration ueber AUSGABEN_RP_*, da sich die .env auf dem Server mit anderen Apps teilt, die RP_ID/RP_ORIGIN bereits belegen - Eigene Tabelle ausgaben_passkeys aus demselben Grund: werte-next nutzt in der gemeinsamen Datenbank RXF bereits die Tabelle passkeys - eslint.config.mjs auf die Flat-Config von eslint-config-next umgestellt Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.3 KiB
6.3 KiB
Wiederverwendbare Authentifizierung
Diese Authentifizierungslösung kann einfach in andere Next.js Projekte übernommen werden.
Komponenten
1. Core Libraries (wiederverwendbar)
/lib/auth.ts- Authentifizierungslogik (Benutzerverwaltung über .env)/lib/session.ts- JWT-basiertes Session-Management/middleware.ts- Route-Schutz Middleware/lib/webauthn.ts- Passkey-Logik (Registrierung & Anmeldung, WebAuthn)/lib/passkeys.ts- Datenbankzugriff auf die Tabelleausgaben_passkeys
2. UI Komponenten (wiederverwendbar)
/app/login/page.tsx- Login-Seite (Passwort + „Mit Passkey anmelden")/app/login/actions.ts- Server Actions für Login/Logout/components/LogoutButton.tsx- Logout-Button Komponente/components/Passkeys.tsx- Verwaltung der eigenen Passkeys (Tab Einstellungen)
3. API-Routen (Passkeys)
/app/api/passkey/route.ts- eigene Passkeys auflisten (GET) / löschen (DELETE)/app/api/passkey/register/route.ts- Passkey registrieren (Session erforderlich)/app/api/passkey/authenticate/route.ts- Anmeldung per Passkey (öffentlich)
Installation in neuen Projekten
1. Dependencies installieren
npm install jose bcryptjs
npm install --save-dev @types/bcryptjs
# Für Passkeys (WebAuthn):
npm install @simplewebauthn/server @simplewebauthn/browser
2. Dateien kopieren
Kopiere folgende Dateien in dein neues Projekt:
lib/auth.tslib/session.tsmiddleware.tsapp/login/(gesamter Ordner)scripts/generate-password.js(Passwort-Hash Generator)components/LogoutButton.tsx(optional)
3. Passwort-Hashes generieren
Verwende das mitgelieferte Script, um sichere Passwort-Hashes zu erstellen:
# Interactive Mode
node scripts/generate-password.js
# Mit Passwort als Argument
node scripts/generate-password.js meinPasswort123
Das Script gibt einen bcrypt-Hash aus, den du in der .env verwenden kannst.
4. Umgebungsvariablen einrichten
Füge zu deiner .env hinzu:
# Authentifizierung
# Format: username:passwordHash,username2:passwordHash2
# Verwende 'node scripts/generate-password.js' um Hashes zu generieren
AUTH_USERS=admin:$2b$10$DKLO7uQPmdAw9Z64NChro...,user1:$2b$10$K613Z70Hodr6xyEh10Mw2u...
# Secret Key für JWT (unbedingt ändern in Production!)
AUTH_SECRET=your-super-secret-key-change-this
# Passkeys (WebAuthn) — RP_ID = nur der Host, RP_ORIGIN = volle URL
RP_ID=ausgaben.fuerst-stuttgart.de
RP_ORIGIN=https://ausgaben.fuerst-stuttgart.de
RP_NAME=Ausgaben-Log
5. Logout-Button einbinden (optional)
import LogoutButton from '@/components/LogoutButton';
// In deiner Komponente:
<LogoutButton />
Konfiguration
Benutzer hinzufügen/entfernen
- Generiere einen Passwort-Hash:
node scripts/generate-password.js neuesPasswort
- Editiere die
AUTH_USERSVariable in der.env:
AUTH_USERS=user1:$2b$10$hash1...,user2:$2b$10$hash2...,user3:$2b$10$hash3...
Authentifizierung deaktivieren
Entferne die AUTH_USERS Variable oder setze sie auf einen leeren String:
AUTH_USERS=
Session-Dauer anpassen
Editiere in lib/session.ts:
const SESSION_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 Tage
Geschützte Routen anpassen
Editiere in middleware.ts die publicPaths:
const publicPaths = ['/login', '/api/passkey/authenticate', '/public-page'];
Die Passkey-Anmeldung muss ohne Session erreichbar sein.
Passkeys verwalten
- Registrieren: angemeldet im Tab Einstellungen → „Passkey hinzufügen".
- Anmelden: auf der Login-Seite „Mit Passkey anmelden".
- Speicherung: Tabelle
ausgaben_passkeys(siehecreate_table.sql), wird beim ersten Zugriff automatisch angelegt, sofern der DB-Benutzer CREATE-Rechte hat. Wichtig bei Übernahme in ein anderes Projekt: Teilen sich mehrere Apps eine Datenbank, braucht jede App einen eigenen Tabellennamen (TABLEinlib/passkeys.ts) — sonst sehen und löschen sie gegenseitig ihre Passkeys. RP_IDmuss exakt dem Hostnamen entsprechen (ohne Protokoll/Port). Ändert sichRP_ID, werden bereits registrierte Passkeys ungültig.- WebAuthn funktioniert nur über HTTPS oder auf
localhost.
Sicherheitshinweise
- AUTH_SECRET ändern: Verwende in Production einen starken, zufälligen Schlüssel
- HTTPS verwenden: In Production immer HTTPS aktivieren
- Passwort-Hashing: Passwörter werden mit bcrypt gehashed (10 Salt Rounds)
- Keine Klartext-Passwörter: Verwende immer das Script zur Hash-Generierung
Passwort-Hash Generator
Das Script scripts/generate-password.js verwendet bcrypt mit 10 Salt Rounds, um sichere Passwort-Hashes zu erstellen.
Verwendung
Interactive Mode (empfohlen für sensible Passwörter):
npm run generate-password
# oder
node scripts/generate-password.js
# Passwort wird interaktiv abgefragt
Mit Argument:
npm run generate-password -- meinPasswort
# oder
node scripts/generate-password.js meinPasswort
Ausgabe
🔐 Generiere Passwort-Hash...
✅ Hash generiert:
────────────────────────────────────────────────────────────────────────────────
$2b$10$DKLO7uQPmdAw9Z64NChro.8mOsnqZQaRZjctWDojIkK926ROBVyJW
────────────────────────────────────────────────────────────────────────────────
📝 Verwende diesen Hash in der .env Datei:
AUTH_USERS=username:$2b$10$DKLO7uQPmdAw9Z64NChro.8mOsnqZQaRZjctWDojIkK926ROBVyJW
Erweiterte Verwendung
Session-Informationen abrufen
import { getSession } from '@/lib/session';
const session = await getSession();
if (session) {
console.log('Eingeloggt als:', session.username);
}
Programmatisch prüfen, ob authentifiziert
import { isAuthenticated } from '@/lib/session';
const authenticated = await isAuthenticated();
In Server Components
import { getSession } from '@/lib/session';
import { redirect } from 'next/navigation';
export default async function ProtectedPage() {
const session = await getSession();
if (!session) {
redirect('/login');
}
return <div>Hallo {session.username}!</div>;
}