Files
Lageplan/package.json
Pepe Ziberi f588b6d361 feat(auth): Zwei-Faktor-Authentisierung — TOTP (Authenticator-App) (v1.8.0)
Freiwillige MFA, du kannst sie für dich aktivieren; Login verlangt danach den zweiten Faktor.

- Schema/Migration: User.totpSecret/totpConfirmedAt/mfaBackupCodes + Tabelle webauthn_credentials
  (WebAuthn folgt), otplib v12
- lib/mfa.ts: TOTP-Secret/QR-URI/Verify + bcrypt-gehashte Backup-Codes
- lib/auth.ts: buildSessionForUser, createMfaToken/verifyMfaToken (10-Min-Zwischentoken), userHasMfa
- Login-Flow: nach Passwort -> bei aktiver MFA kein Cookie, sondern mfaToken; öffentlicher
  /api/auth/mfa-login/verify prüft TOTP ODER Backup-Code und setzt dann die Session (Rate-Limit)
- Einrichtung in /settings: QR scannen, Code bestätigen, Backup-Codes (einmalig), deaktivieren
- Admin-Reset-API: /api/admin/users/[id]/mfa-reset (SERVER_ADMIN alle, TENANT_ADMIN eigene Org)
- Login-Seite: zweiter Schritt für Code/Backup-Code
- SMS bewusst weggelassen (Kosten/SIM-Swap)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 18:06:24 +02:00

104 lines
3.2 KiB
JSON

{
"name": "lageplan",
"version": "1.8.0",
"description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "vitest run",
"test:watch": "vitest",
"db:generate": "prisma generate",
"db:push": "prisma db push",
"db:migrate": "prisma migrate dev",
"db:migrate:prod": "prisma migrate deploy",
"db:seed": "node prisma/seed.js",
"db:studio": "prisma studio",
"postinstall": "prisma generate",
"db:seed:icons": "node prisma/seed-icons-only.js",
"db:seed:safe": "node prisma/seed.js",
"repair:features:dry-run": "node prisma/repair-features.js --dry-run",
"repair:features:apply": "node prisma/repair-features.js --apply",
"recover:symbols:dry-run": "node prisma/recover-symbols.js --dry-run",
"recover:symbols:apply": "node prisma/recover-symbols.js --apply"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@prisma/client": "^5.9.0",
"@radix-ui/react-alert-dialog": "^1.1.0",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.0",
"@radix-ui/react-dropdown-menu": "^2.1.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.0",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-select": "^2.1.0",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.0",
"@radix-ui/react-tooltip": "^1.1.0",
"@react-pdf/renderer": "^4.3.2",
"bcryptjs": "^2.4.3",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"html2canvas": "^1.4.1",
"jose": "^5.2.0",
"jspdf": "^2.5.1",
"lucide-react": "^0.312.0",
"maplibre-gl": "^4.0.0",
"minio": "^7.1.3",
"next": "^15.1.0",
"nodemailer": "^6.9.8",
"otplib": "^12.0.1",
"qrcode": "^1.5.4",
"rdndmb-html5-to-touch": "^9.0.0",
"react": "^19.0.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^9.0.0",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^19.0.0",
"react-moveable": "^0.56.0",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"stripe": "^20.3.1",
"tailwind-merge": "^2.2.1",
"tailwindcss-animate": "^1.0.7",
"uuid": "^9.0.1",
"zod": "^3.22.4",
"zustand": "^5.0.11"
},
"prisma": {
"seed": "node prisma/seed.js"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/node": "^20.11.0",
"@types/qrcode": "^1.5.6",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/uuid": "^9.0.7",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-next": "^15.1.0",
"postcss": "^8.4.33",
"prisma": "^5.9.0",
"sharp": "^0.34.5",
"tailwindcss": "^3.4.1",
"tsx": "^4.7.0",
"typescript": "^5.3.3",
"vitest": "^4.1.10"
},
"browserslist": [
"last 2 Chrome versions",
"last 2 Firefox versions",
"last 2 Safari versions",
"last 2 Edge versions"
]
}