Security review findings (HIGH-1/2/3, MEDIUM):
- Rate limiting via @nestjs/throttler: global 100 req/min default, stricter
10 req/min on login and refresh. Also closes the MEDIUM finding (public
batch-lookup enumeration) since it now sits behind the global limit too.
Trust the K8s ingress's single proxy hop so throttling keys off the real
client IP, not the ingress pod's IP.
- Auth events (login success/failure, logout, token refresh) are now
audit-logged via new AuditAction values, closing the gap where the
project's own audit-log principle never actually covered the Auth module.
- Refresh-token cookie now defaults to Secure=true (opt out only via
COOKIE_SECURE=false for local HTTP dev), instead of keying off NODE_ENV
which was never set in the K8s deploy templates. Added a TLS block +
cert-manager annotation to both the backend and frontend ingress
templates, which previously had no HTTPS termination configured at all.
/login: Formular gegen AuthProvider (Access-Token im Memory, Refresh
per httpOnly-Cookie), Fehlermeldung bei falschen Zugangsdaten.
(app)/layout.tsx: geschuetztes Layout, leitet ohne Session zu /login um,
Header mit Nutzer-E-Mail + Abmelden. settings/layout.tsx: zusaetzliches
Admin-Gate fuer /settings/users.
/settings/users: Liste, Anlegen-Dialog (shadcn/Base UI Dialog+Select),
Deaktivieren - alles ueber TanStack Query gegen die Users-API.
proxy.ts (Next 16 Nachfolger von middleware.ts): optimistische
Cookie-Praesenz-Pruefung fuer /login und /settings/*, echte
Autorisierung bleibt serverseitig (RolesGuard).
Backend-Fixes waehrend Browser-Verifikation gefunden und behoben:
- CORS fehlte komplett (app.enableCors, FRONTEND_ORIGIN) - jeder
Cross-Origin-Request von :3000 nach :3001 wurde geblockt.
- React-StrictMode-Doppel-Effect liess zwei parallele Refresh-Aufrufe
laufen; da Refresh-Tokens rotierend/einmalig sind, gewann einer und
der andere bekam 401 -> Nutzer flog nach jedem Reload aus der Session.
Fix: AbortController im Session-Restore-Effect.
- Manifest verwies auf nicht existente PNG-Icons (404); durch
public/icon.svg ersetzt.
7 Vitest-Tests (Login-Formular, Users-Liste, JWT-Decode), Lint,
Typecheck und Build gruen. Kompletter Flow (Login, Reload-Persistenz,
Benutzer anlegen/deaktivieren, Abmelden, Redirect ohne Session) live
im Browser gegen den echten Server verifiziert.
ARCHITECTURE.md §3 legt fest: Access-Token im Memory/Context, Refresh-Token
als httpOnly-Cookie. Vorherige Implementierung gab beide Tokens im JSON-Body
zurueck - vor dem Frontend-Aufbau (das genau gegen diesen Vertrag baut)
korrigiert.
/api/v1/auth/login und /refresh setzen jetzt ein httpOnly-Cookie
(Path /api/v1/auth, SameSite=Lax, secure in production) und liefern nur
noch { accessToken } im Body. /refresh und /logout lesen den Refresh-Token
aus dem Cookie statt aus dem Request-Body.
e2e-Tests und Live-Smoke-Test (curl mit Cookie-Jar) verifizieren
Login/Rotation/Logout-Invalidierung. 12 e2e- + 26 Unit-Tests, Lint,
Typecheck und Build gruen.
AuthModule: Login/Refresh/Logout mit JWT-Access-Token + rotierendem,
gehashtem Refresh-Token (RefreshToken-Tabelle). JwtAuthGuard + RolesGuard
global via APP_GUARD registriert (gilt fuer jeden kuenftigen Endpunkt),
@Public()-Decorator als Ausnahme (Auth-Endpunkte, /health).
UsersModule: CRUD + Deactivate, nur ADMIN-Rolle, Passwoerter mit bcrypt
gehasht, nie im Response enthalten.
AuditModule: AuditLogService (Grundstruktur), wird von UsersService bei
create/update/deactivate injiziert und geschrieben.
Geteilte App-Konfiguration (Prefix/Versioning/Pipes/Helmet) aus main.ts
in src/app.config.ts extrahiert; e2e-Tests nutzten das vorher nicht und
bekamen dadurch 404 auf jede /api/v1/... Route - jetzt ueber
test/utils/create-test-app.ts geteilt, betrifft alle kuenftigen
e2e-Tests gleichermassen.
prisma/seed.ts legt einen Admin-User fuer lokale Entwicklung an.
26 Unit-Tests + 11 e2e-Tests (gegen echte Postgres-Testdatenbank) gruen,
Lint/Typecheck/Build gruen, Login/Refresh/Logout/Rollenpruefung manuell
gegen laufenden Server verifiziert.