--- phase: quick-260917-gyd plan: 01 subsystem: auth tags: [next.js, middleware, jwt, i18n, vitest] requires: [] provides: - "safe-next.ts: buildNextParam()/sanitizeNextPath() als reine, getestete Funktionen fuer den Ruecksprung-Parameter (Open-Redirect-Schutz)" - "fetchSessionState() in auth-actions.ts: klassifiziert die Sitzung (authenticated/unauthenticated/unavailable) und loescht das Cookie nur bei nachweislich toter Sitzung" - "Header-Waechter erkennt tote Sitzung und leitet auf /login?next=… um, bleibt bei API-Ausfall still" - "Einstellungen → Widgets vollstaendig uebersetzt (i18n statt hartkodiertem Englisch)" affects: [web-auth, web-settings] actuals: tokens: 6602 tasks: 3 commits: 3 plan_head_before: 4778824c73caafbf859c71c5c5a1c40401732296 tech-stack: added: [] patterns: - "safe-next.ts: reine Funktionen ohne Node-/DOM-APIs, damit ein Modul sowohl von der Edge-Middleware als auch vom Client importiert werden kann" - "fetchSessionState() klassifiziert API-Antworten in drei Zustaende (authenticated/unauthenticated/unavailable) statt eines binaeren Erfolg/Misserfolg, um Abmelde-Schleifen bei API-Ausfaellen zu vermeiden" key-files: created: - apps/web/src/lib/safe-next.ts - apps/web/src/lib/safe-next.test.ts - apps/web/src/lib/auth-actions.test.ts - apps/web/src/components/layout/header.test.tsx modified: - apps/web/src/middleware.ts - apps/web/src/app/(auth)/login/page.tsx - apps/web/src/lib/auth-actions.ts - apps/web/src/components/layout/header.tsx - apps/web/src/app/(portal)/settings/dashboard/page.tsx - apps/web/src/messages/de.json - apps/web/src/messages/en.json - CHANGELOG.md key-decisions: - "fetchCurrentUser() bleibt unveraendert (Signatur und Verhalten) — change-password/page.tsx, account-settings-form.tsx und deren Test-Mock haengen davon ab; fetchSessionState() ist additiv daneben entstanden, mit akzeptierter kleiner Code-Duplikation der fetch-Zeilen" - "Cookie-Loeschung bei toter Sitzung passiert ausschliesslich in der Server Action fetchSessionState() (cookieStore.delete() ist nur dort erlaubt), der Header liest nur den klassifizierten Zustand und navigiert" - "login/page.tsx liest next erst beim Absenden aus window.location.search statt per useSearchParams(), weil der Hook in Next 15 eine Suspense-Grenze braucht und sonst next build fuer die statisch vorgerenderte Login-Seite abbricht" patterns-established: - "Sitzungswaechter-Muster: dreiwertige Klassifikation (authenticated/unauthenticated/unavailable) statt AuthUser | null, damit ein API-Ausfall nicht als tote Sitzung fehlinterpretiert wird" requirements-completed: [QUICK-260917-GYD] coverage: - id: D1 description: "Middleware haengt next-Parameter (Pfad + Query ohne _rsc) an beide Login-Umleitungen an; nicht fuer / und /login…" requirement: QUICK-260917-GYD verification: - kind: unit ref: "apps/web/src/lib/safe-next.test.ts (9 Faelle)" status: pass human_judgment: false - id: D2 description: "Anmeldeseite springt nach Erfolg auf den bereinigten next-Wert; unsichere Werte fallen auf / zurueck" requirement: QUICK-260917-GYD verification: - kind: unit ref: "apps/web/src/lib/safe-next.test.ts (sanitizeNextPath-Faelle)" status: pass human_judgment: false - id: D3 description: "fetchSessionState() loescht das Cookie nur bei 401/403/leerer 200-Antwort, meldet 5xx/Netzwerkfehler als unavailable ohne Cookie-Loeschung" requirement: QUICK-260917-GYD verification: - kind: unit ref: "apps/web/src/lib/auth-actions.test.ts (8 Faelle)" status: pass human_judgment: false - id: D4 description: "Header leitet bei toter Sitzung per Vollnavigation auf /login?next=… um und bleibt bei API-Ausfall still (Avatar zeigt weiterhin ?)" requirement: QUICK-260917-GYD verification: - kind: unit ref: "apps/web/src/components/layout/header.test.tsx (4 Faelle)" status: pass human_judgment: false - id: D5 description: "Einstellungen → Widgets zeigt Lade- und Leerhinweis ueber i18n (de/en) statt hartkodiertem Englisch" requirement: QUICK-260917-GYD verification: - kind: other ref: "grep-Gates auf settings/dashboard/page.tsx + de.json/en.json settings.widgets.empty (Task-3-verify)" status: pass human_judgment: false - id: D6 description: "Browser-Nachweis: ohne Sitzung /settings/general/desktop aufrufen → Login-URL traegt next, nach Anmeldung landet man auf der Desktop-Seite; Sitzungscookie mit nicht mehr existierender Benutzer-ID → Umleitung zur Anmeldeseite statt ?-Avatar" verification: [] human_judgment: true rationale: "Erfordert einen echten Browser-Lauf gegen die laufende API/DB (Orchestrator-Aufgabe, nicht Teil dieses Ausfuehrungsplans laut -Sektion des Plans)" duration: 7min completed: 2026-09-17 status: complete --- # Quick Task 260917-gyd: Ruecksprung nach Anmeldung, Sitzungswaechter bei toter API-Sitzung, Widgets-Seite uebersetzt Summary **`next`-Parameter fuer den Ruecksprung nach Anmeldung (safe-next.ts, Open-Redirect-getestet), `fetchSessionState()` unterscheidet tote Sitzung von API-Ausfall und leitet den Header ab, Einstellungen → Widgets durchgaengig deutsch.** ## Performance - **Duration:** 7 min - **Started:** 2026-09-17T10:24:00Z - **Completed:** 2026-09-17T10:30:49Z - **Tasks:** 3 - **Files modified:** 12 ## Accomplishments - Middleware und Anmeldeseite tragen jetzt einen getesteten `next`-Ruecksprung durch — der Desktop-App-Link „Update herunterladen" landet nach der Anmeldung wieder auf der urspruenglich angeforderten Seite - `fetchSessionState()` erkennt eine tote Sitzung (401/403/leere 200-Antwort) zuverlaessig und trennt sie von einem stillen API-Ausfall (5xx/Netzwerkfehler) — der Header meldet sich bei toter Sitzung ab statt ein halb angemeldetes „?"-Avatar-Portal zu zeigen - Einstellungen → Widgets zeigt Lade- und Leerhinweis vollstaendig ueber i18n ## Task Commits Each task was committed atomically: 1. **Task 1: Ruecksprung — `next`-Parameter in Middleware und Anmeldeseite** - `4b279ea` (feat) 2. **Task 2: Sitzungswaechter — `fetchSessionState()` unterscheidet tote Sitzung von API-Ausfall** - `474d170` (feat) 3. **Task 3: Widgets-Seite uebersetzen, CHANGELOG ergaenzen, Gesamtlauf** - `2868ffe` (feat) _Alle drei Tasks folgten TDD (RED zuerst bei Task 1 und 2, Task 3 ohne `tdd="true"`)._ ## Files Created/Modified - `apps/web/src/lib/safe-next.ts` - `buildNextParam()`/`sanitizeNextPath()`, reine Funktionen, Open-Redirect-Schutz - `apps/web/src/lib/safe-next.test.ts` - 9 Testfaelle beider Funktionen - `apps/web/src/middleware.ts` - beide Login-Umleitungen ueber gemeinsamen `redirectToLogin()`-Helfer mit `next` - `apps/web/src/app/(auth)/login/page.tsx` - springt nach Erfolg auf `sanitizeNextPath(next)` - `apps/web/src/lib/auth-actions.ts` - `fetchSessionState()` (neu, additiv), `fetchCurrentUser()` unveraendert - `apps/web/src/lib/auth-actions.test.ts` - 8 Klassifikations-Testfaelle - `apps/web/src/components/layout/header.tsx` - Waechter im useEffect ersetzt `fetchCurrentUser` durch `fetchSessionState` - `apps/web/src/components/layout/header.test.tsx` - 4 Testfaelle (authenticated/unauthenticated auf Unterseite/auf `/`/unavailable) - `apps/web/src/app/(portal)/settings/dashboard/page.tsx` - `tCommon('loading')` und `t('widgets.empty')` statt Festtext - `apps/web/src/messages/de.json`, `en.json` - `settings.widgets.empty` (additiv) - `CHANGELOG.md` - drei Stichpunkte unter „Behoben" ## Decisions Made - `fetchCurrentUser()` unangetastet gelassen (Signatur- und Verhaltensgarantie fuer change-password/page.tsx und account-settings-form.tsx), `fetchSessionState()` daneben additiv mit kleiner Code-Duplikation der fetch-Aufrufzeilen - Cookie-Loeschung ausschliesslich in der Server Action, nicht im Client-Header - `next`-Wert wird in login/page.tsx erst beim Absenden aus `window.location.search` gelesen (kein `useSearchParams()`, wegen fehlender Suspense-Grenze bei der statisch vorgerenderten Anmeldeseite) ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered - `bug-report-button.test.tsx` (nicht Teil dieses Plans) schlug einmal im Gesamtlauf flakey fehl (Checkbox-Timing) und lief beim naechsten Lauf sowie isoliert gruen — ausserhalb des Scopes dieses Plans, nicht auto-gefixt, kein Deviation-Eintrag noetig. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Browser-Nachweis (ohne Sitzung `/settings/general/desktop` aufrufen, tote Sitzung simulieren, Widgets-Seite pruefen) steht laut Plan beim Orchestrator aus, nicht Teil dieser Ausfuehrung. - Keine Blocker fuer Folgearbeiten; `fetchCurrentUser()` bleibt fuer bestehende Aufrufer nutzbar. --- *Phase: quick-260917-gyd* *Completed: 2026-09-17* ## Self-Check: PASSED - FOUND: apps/web/src/lib/safe-next.ts - FOUND: apps/web/src/lib/safe-next.test.ts - FOUND: apps/web/src/middleware.ts - FOUND: apps/web/src/app/(auth)/login/page.tsx - FOUND: apps/web/src/lib/auth-actions.ts - FOUND: apps/web/src/lib/auth-actions.test.ts - FOUND: apps/web/src/components/layout/header.tsx - FOUND: apps/web/src/components/layout/header.test.tsx - FOUND: apps/web/src/app/(portal)/settings/dashboard/page.tsx - FOUND: apps/web/src/messages/de.json - FOUND: apps/web/src/messages/en.json - FOUND: CHANGELOG.md - FOUND commit: 4b279ea - FOUND commit: 474d170 - FOUND commit: 2868ffe