Files
Florian-netz/package.json
Matthias Hochmeister ae5d3589c3 Workstream 3: Authentifizierung & Zugriffskontrolle (Phase 2)
Einheitliche Auth.js-v5-Sitzung (role + brigadeId) über Authentik-OIDC
(Platform-Admins) und Credentials/argon2id (Wehr-Konten). Default-deny
dreifach: Middleware + Layout-Guard im (app)-Segment + 401/403 in API-Routen
und Guards in Server Actions.

- src/lib/auth/roles.ts: Rollen als Single Source of Truth (aus DB-Enum
  abgeleitet) + canAccessBrigade-Wehr-Scoping.
- src/lib/auth/password.ts: argon2id mit OWASP-Minima (Node-only).
- src/lib/auth/rate-limit.ts: Sliding Window (5 Fehlversuche/15 min) auf
  login_attempts; greift im authorize-Callback (beide Login-Pfade).
- src/auth.config.ts: Edge-sicher (kein @/db, kein argon2), Cookie-secure/
  __Secure- umgebungsabhaengig (isHttps).
- src/auth.ts: Credentials + DB-Lookup + Rate-Limit + Authentik-signIn-Gate.
- src/middleware.ts: Allowlist inkl. api/auth, api/health, login, Statics.
- src/lib/auth/guards.ts: requireSession/requireRole/requirePlatformAdmin/
  requireWehrAdmin/requireOwnBrigade + API-Varianten (401/403 ohne Daten-Leak).
- (app)/layout.tsx: requireSession() als erste Zeile aktiviert.
- (auth)/login: page + login-form + Server Actions (Zod, Authentik + lokal).
- api/auth/[...nextauth]/route.ts; api/health/route.ts (anonym 200).
- scripts/seed-auth.ts: idempotenter Erst-Admin-Seed.
- Typen: src/types/next-auth.d.ts.
- Tests: Unit (password/roles/rate-limit) gruen; E2E-Gating-Spec geschrieben
  (deferred, kein Server/DB in Sandbox).

Offline verifiziert: tsc --noEmit, next lint, next build, drizzle-kit check,
vitest (13 Unit-Tests) je ohne Fehler; Edge-Safety-Grep ohne DB/argon2-Import.

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

62 lines
1.7 KiB
JSON

{
"name": "floriannetz",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"db:generate": "drizzle-kit generate",
"db:migrate": "tsx scripts/migrate.ts",
"db:seed-auth": "tsx scripts/seed-auth.ts",
"test:e2e:gating": "playwright test tests/e2e/auth-gating.spec.ts",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:check": "drizzle-kit check"
},
"dependencies": {
"@node-rs/argon2": "^2.0.2",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-slider": "^1.2.3",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.39.3",
"next": "^15.2.0",
"next-auth": "^5.0.0-beta.31",
"pg": "^8.13.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^3.0.2",
"zod": "^3.24.2"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@playwright/test": "^1.60.0",
"@types/node": "^22.13.5",
"@types/pg": "^8.11.11",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"autoprefixer": "^10.4.20",
"drizzle-kit": "^0.30.4",
"eslint": "^9.21.0",
"eslint-config-next": "^15.2.0",
"postcss": "^8.5.3",
"prettier": "^3.5.2",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^3.4.17",
"tsx": "^4.19.2",
"typescript": "^5.7.3",
"vitest": "^3.0.7"
}
}