feat(auth): KTO-01 Passwort zurücksetzen (01c) (4/4) #36

Merged
cschlaefke merged 5 commits from feature/1-01c-passwort-reset into main 2026-11-03 09:21:44 +00:00
Owner

Zusammenfassung

Passwort-Reset für #1 (KTO-01, PR 01c): ForgotPasswordScreen, Recovery-Link im EmailLinkScreen, ResetPasswordScreen mit Recovery-Guard, Abbruch mit signOut und Erfolgsmeldung. Dazu die Folgepunkte aus dem 01b-Review (from-Weitergabe, Resend-Aktion deaktivieren statt ausblenden). Platzhalter-Routen sind entfernt. Dies ist der letzte App-Teil von #1; Playwright-E2E (T3) und der CI-Job D2 folgen separat.

Closes #1

Spec: docs/specs/1-auth-registrierung-anmeldung.md

Akzeptanzkriterien (Anteil 01c)

  • 4 Passwort vergessen: gleiche Reaktion für bekannte/unbekannte Adresse; emailSendLimited wie Erfolg (F12)
  • 8 Reset-Link: Knopf nötig, otp_expired führt zu „Neuen Link anfordern“; Gültigkeit 1 h und Einmaligkeit serverseitig (API-Test in T3)
  • 9 Nach Passwortwechsel Erfolgsmeldung; ehrlicher Text („kann bis zu einer Stunde dauern“); Abmeldung anderer Geräte serverseitig, E2E in T3
  • 5, 6, 7 (01b) und 1–3, 10, 11 (01a) bereits gemergt

Abdeckungsmatrix

AK Test(s)
4 / F12 forgot_password_screen_test: bekannte, unbekannte Adresse und emailSendLimited zeigen exakt dieselben sichtbaren Texte, kein Drosselungshinweis
8 email_link_*_test (Recovery, otp_expired), reset_password_controller_test
9 auth_router_test: voller Ablauf mit echtem Repository und gemocktem GoTrue-Client (Link, Reset-Seite, Navigation blockiert, Speichern, Erfolgsansicht, „Weiter“, Abbrechen -> signOut -> /login); userUpdated auch asynchron nach updateUser
Guard Tracker-/Repository-Tests: finishRecovery ohne Passwortwechsel bleibt PasswordRecovery; angemeldeter Nutzer erreicht /reset-password nicht
Querschnitt a11y-Guidelines, textScaler 2.0, de/en, hell/dunkel, toString() ohne Zugangsdaten

492 Tests grün.

Abweichungen von der Spec – bitte ausdrücklich freigeben (Spec § 5.3–5.5, § 10)

  1. Recovery-Guard endet nicht mit userUpdated, sondern mit finishPasswordRecovery() (Knopf „Weiter“ in der Erfolgsansicht), signedOut oder Wegfall der Sitzung. Grund: gotrue sendet userUpdated asynchron nach updateUser; die Regel „angemeldet auf /reset-password -> /“ hätte die Erfolgsmeldung (Krit. 9) entfernt. AuthStatus.passwordRecovery hat dafür das Feld passwordChanged; finishRecovery greift nur, wenn es gesetzt ist.
  2. Neue Route /forgot-password/check-email (Modus im Pfad, übersteht Web-Reload; keine Adresse in der URL).
  3. Kein Resend im Reset-Modus (Spec § 5.4): dort steht „Andere E-Mail-Adresse eingeben“.
  4. Erfolgstext Krit. 9: „Passwort geändert. Andere Geräte werden abgemeldet; das kann bis zu einer Stunde dauern.“ (Test: kein „sofort“).

Definition of Done

  • dart format, flutter analyze ohne Befund, flutter test grün
  • l10n DE/EN, keine hartkodierten Strings
  • Barrierefreiheit: Live-Region, Fokus auf „Weiter“, Touch-Ziele, Kontrast als Guideline-Tests
  • Doku: Spec, docs/design/auth-screens.md, docs/design-system.md
  • Android/iOS nicht gebaut; nicht im echten Browser/Screenreader geprüft
  • Playwright-E2E (T3) und CI-Job D2 folgen als eigener PR

Review

code-reviewer: kein Blocker; wichtige Punkte behoben (7c2f684): finishRecovery setzt passwordChanged voraus, Erfolg wird vom Repository selbst markiert (kein Warten auf das Ereignis), Formular bleibt nach Erfolg gesperrt, Spec-Widerspruch bereinigt, from durch Reset-Modus weitergereicht, _neverFromPaths ergänzt, gemeinsamer Passwort-Fehler-Helfer, Fokus auf „Weiter“.

Risiken / Folge

  • Mögliche Konten-Aufdeckung bei SMTP-Fehlern: Liefert GoTrue bei Mailfehler für bestehende Adressen einen Fehler, für unbekannte aber 200, zeigt ForgotPasswordController (Fall default) ein Banner. Dann wäre die Existenz erkennbar. Nicht nachgewiesen; nur mit abgeschaltetem Mailserver prüfbar. Vorschlag: Folge-Issue needs-clarification.
  • Die Erfolgsansicht hängt an gotrue-Ereignissen (2.27.3); bei einem Update beobachten.
  • Offene Release-Blocker aus der Spec (D1: Gateway/SMTP/Magic-Link-Sperre, Rechtstexte) bleiben bestehen.

So testest du es lokal

supabase start -x studio,imgproxy,edge-runtime,logflare,vector && supabase db reset
cd app && flutter run -d chrome --web-port 3000 --dart-define-from-file=env/dev.json
# http://localhost:3000/forgot-password, Mails: http://127.0.0.1:54324 (Mailpit)

🤖 Generated with Claude Code

## Zusammenfassung Passwort-Reset für #1 (KTO-01, PR 01c): `ForgotPasswordScreen`, Recovery-Link im `EmailLinkScreen`, `ResetPasswordScreen` mit Recovery-Guard, Abbruch mit `signOut` und Erfolgsmeldung. Dazu die Folgepunkte aus dem 01b-Review (`from`-Weitergabe, Resend-Aktion deaktivieren statt ausblenden). Platzhalter-Routen sind entfernt. Dies ist der letzte App-Teil von #1; Playwright-E2E (T3) und der CI-Job D2 folgen separat. Closes #1 Spec: `docs/specs/1-auth-registrierung-anmeldung.md` ## Akzeptanzkriterien (Anteil 01c) - [x] **4** Passwort vergessen: gleiche Reaktion für bekannte/unbekannte Adresse; `emailSendLimited` wie Erfolg (F12) - [x] **8** Reset-Link: Knopf nötig, `otp_expired` führt zu „Neuen Link anfordern“; Gültigkeit 1 h und Einmaligkeit serverseitig (API-Test in T3) - [x] **9** Nach Passwortwechsel Erfolgsmeldung; ehrlicher Text („kann bis zu einer Stunde dauern“); Abmeldung anderer Geräte serverseitig, E2E in T3 - [x] 5, 6, 7 (01b) und 1–3, 10, 11 (01a) bereits gemergt ## Abdeckungsmatrix | AK | Test(s) | |---|---| | 4 / F12 | `forgot_password_screen_test`: bekannte, unbekannte Adresse und `emailSendLimited` zeigen exakt dieselben sichtbaren Texte, kein Drosselungshinweis | | 8 | `email_link_*_test` (Recovery, `otp_expired`), `reset_password_controller_test` | | 9 | `auth_router_test`: voller Ablauf mit echtem Repository und gemocktem GoTrue-Client (Link, Reset-Seite, Navigation blockiert, Speichern, Erfolgsansicht, „Weiter“, Abbrechen -> `signOut` -> `/login`); `userUpdated` auch asynchron nach `updateUser` | | Guard | Tracker-/Repository-Tests: `finishRecovery` ohne Passwortwechsel bleibt `PasswordRecovery`; angemeldeter Nutzer erreicht `/reset-password` nicht | | Querschnitt | a11y-Guidelines, textScaler 2.0, de/en, hell/dunkel, `toString()` ohne Zugangsdaten | 492 Tests grün. ## Abweichungen von der Spec – **bitte ausdrücklich freigeben** (Spec § 5.3–5.5, § 10) 1. **Recovery-Guard endet nicht mit `userUpdated`**, sondern mit `finishPasswordRecovery()` (Knopf „Weiter“ in der Erfolgsansicht), `signedOut` oder Wegfall der Sitzung. Grund: gotrue sendet `userUpdated` asynchron nach `updateUser`; die Regel „angemeldet auf `/reset-password` -> `/`“ hätte die Erfolgsmeldung (Krit. 9) entfernt. `AuthStatus.passwordRecovery` hat dafür das Feld `passwordChanged`; `finishRecovery` greift nur, wenn es gesetzt ist. 2. **Neue Route `/forgot-password/check-email`** (Modus im Pfad, übersteht Web-Reload; keine Adresse in der URL). 3. **Kein Resend im Reset-Modus** (Spec § 5.4): dort steht „Andere E-Mail-Adresse eingeben“. 4. **Erfolgstext Krit. 9:** „Passwort geändert. Andere Geräte werden abgemeldet; das kann bis zu einer Stunde dauern.“ (Test: kein „sofort“). ## Definition of Done - [x] `dart format`, `flutter analyze` ohne Befund, `flutter test` grün - [x] l10n DE/EN, keine hartkodierten Strings - [x] Barrierefreiheit: Live-Region, Fokus auf „Weiter“, Touch-Ziele, Kontrast als Guideline-Tests - [x] Doku: Spec, `docs/design/auth-screens.md`, `docs/design-system.md` - [ ] Android/iOS nicht gebaut; nicht im echten Browser/Screenreader geprüft - [ ] Playwright-E2E (T3) und CI-Job D2 folgen als eigener PR ## Review `code-reviewer`: kein Blocker; wichtige Punkte behoben (`7c2f684`): `finishRecovery` setzt `passwordChanged` voraus, Erfolg wird vom Repository selbst markiert (kein Warten auf das Ereignis), Formular bleibt nach Erfolg gesperrt, Spec-Widerspruch bereinigt, `from` durch Reset-Modus weitergereicht, `_neverFromPaths` ergänzt, gemeinsamer Passwort-Fehler-Helfer, Fokus auf „Weiter“. ## Risiken / Folge - **Mögliche Konten-Aufdeckung bei SMTP-Fehlern:** Liefert GoTrue bei Mailfehler für bestehende Adressen einen Fehler, für unbekannte aber 200, zeigt `ForgotPasswordController` (Fall `default`) ein Banner. Dann wäre die Existenz erkennbar. Nicht nachgewiesen; nur mit abgeschaltetem Mailserver prüfbar. Vorschlag: Folge-Issue `needs-clarification`. - Die Erfolgsansicht hängt an gotrue-Ereignissen (2.27.3); bei einem Update beobachten. - Offene Release-Blocker aus der Spec (D1: Gateway/SMTP/Magic-Link-Sperre, Rechtstexte) bleiben bestehen. ## So testest du es lokal ```bash supabase start -x studio,imgproxy,edge-runtime,logflare,vector && supabase db reset cd app && flutter run -d chrome --web-port 3000 --dart-define-from-file=env/dev.json # http://localhost:3000/forgot-password, Mails: http://127.0.0.1:54324 (Mailpit) ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cschlaefke added 5 commits 2026-11-03 09:00:28 +00:00
Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ForgotPasswordScreen with neutral reaction (F12), CheckEmail reset mode on
its own path, recovery link handling and ResetPasswordScreen with the
recovery guard held until the user confirms the success message. Carries
the from parameter between login, register and forgot password. Removes
the route placeholders.

Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fix(auth): address review findings for password reset
All checks were successful
CI / flutter (pull_request) Successful in 1m13s
CI / web-build (pull_request) Successful in 55s
CI / supabase (pull_request) Successful in 3s
CI / supabase-tests (pull_request) Successful in 30s
7c2f684098
Finish the recovery guard only after the password change, mark the change
in the repository so a late userUpdated event is harmless, keep the form
locked after success, carry from through the reset check-email page,
share the password error texts and focus the continue button.

Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
cschlaefke added the
status/ready-for-merge
label 2026-11-03 09:21:02 +00:00
Author
Owner

Review- und CI-Ergebnis (01c Passwort zurücksetzen)

CI (Commit 7c2f684): alle 4 Jobs grün – flutter (1m13s), web-build (55s), supabase (3s), supabase-tests (30s).

Code-Review (Agent code-reviewer): kein Blocker. Wichtige Punkte in 7c2f684 behoben:

  • finishRecovery beendet den Guard nur nach Passwortwechsel (Tracker- und Repository-Test).
  • Erfolg wird vom Repository selbst markiert; Test mit asynchronem userUpdated nach updateUser; Formular bleibt nach Erfolg gesperrt.
  • Spec § 5.5 widerspruchsfrei.
  • from läuft durch den Reset-Modus, _neverFromPaths ergänzt, gemeinsamer Passwort-Fehler-Helfer, Fokus auf „Weiter“.

Spec-Abweichungen: von Chris im Chat freigegeben (Recovery-Guard mit „Weiter“, Route /forgot-password/check-email, kein Resend im Reset-Modus, Erfolgstext Krit. 9).

Sicherheits-Folgeissue: Mögliche Konten-Aufdeckung bei SMTP-Fehlern (Passwort-Reset/Registrierung) ist als #37 angelegt (prio/must, needs-clarification), vor Produktiv-Release zu klären.

Bitte beachten:

  • Nicht getestet: Android/iOS, echter Browser und Screenreader.
  • Playwright-E2E (T3) und CI-Job D2 folgen als eigener PR; Release-Blocker D1 und Rechtstexte bleiben offen.
  • Der PR schließt #1 (Closes #1).

Merge durch Chris.

## Review- und CI-Ergebnis (01c Passwort zurücksetzen) **CI (Commit `7c2f684`):** alle 4 Jobs grün – `flutter` (1m13s), `web-build` (55s), `supabase` (3s), `supabase-tests` (30s). **Code-Review (Agent `code-reviewer`):** kein Blocker. Wichtige Punkte in `7c2f684` behoben: - `finishRecovery` beendet den Guard nur nach Passwortwechsel (Tracker- und Repository-Test). - Erfolg wird vom Repository selbst markiert; Test mit asynchronem `userUpdated` nach `updateUser`; Formular bleibt nach Erfolg gesperrt. - Spec § 5.5 widerspruchsfrei. - `from` läuft durch den Reset-Modus, `_neverFromPaths` ergänzt, gemeinsamer Passwort-Fehler-Helfer, Fokus auf „Weiter“. **Spec-Abweichungen:** von Chris im Chat freigegeben (Recovery-Guard mit „Weiter“, Route `/forgot-password/check-email`, kein Resend im Reset-Modus, Erfolgstext Krit. 9). **Sicherheits-Folgeissue:** Mögliche Konten-Aufdeckung bei SMTP-Fehlern (Passwort-Reset/Registrierung) ist als #37 angelegt (`prio/must`, `needs-clarification`), vor Produktiv-Release zu klären. **Bitte beachten:** - Nicht getestet: Android/iOS, echter Browser und Screenreader. - Playwright-E2E (T3) und CI-Job D2 folgen als eigener PR; Release-Blocker D1 und Rechtstexte bleiben offen. - Der PR schließt #1 (`Closes #1`). Merge durch Chris.
cschlaefke merged commit 6bcbaff95a into main 2026-11-03 09:21:44 +00:00
cschlaefke deleted branch feature/1-01c-passwort-reset 2026-11-03 09:21:44 +00:00
Sign in to join this conversation.
No description provided.