feat(auth): KTO-01 Anmeldung (01b) – LoginScreen (3/4) #35

Merged
cschlaefke merged 3 commits from feature/1-01b-anmeldung into main 2026-11-03 08:33:36 +00:00
Owner

Zusammenfassung

Anmeldung für #1 (KTO-01, PR 01b): LoginScreen mit Controller und State, allgemeiner Fehlertext, Hinweis bei unbestätigtem Konto mit erneutem Senden, Drosselungs- und Offline-Meldung, Autofill, sichere from-Weiterleitung. Passwort-Reset (01c) folgt; dieser PR schließt #1 nicht.

Refs #1

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

Akzeptanzkriterien (Anteil 01b)

  • 5 Angemeldet bleiben: UI und Weiterleitung (from); dauerhafte Sitzung wird per E2E (T3) geprüft
  • 6 Unbestätigtes Konto: Hinweis + „Bestätigungs-E-Mail erneut senden“; emailSendLimited zeigt Drosselungshinweis (F12)
  • 7 Falsche E-Mail und falsches Passwort zeigen denselben Text (nur Banner, nie am Feld); 429 zeigt Drosselungshinweis. Echte Drosselung (Login #31) folgt in T3/D2
  • 8, 9 Passwort zurücksetzen (01c)

Abdeckungsmatrix

AK Test(s)
5 login_controller_test (Erfolg, from), login_screen_test (Weiterleitung, Autofill-Abschluss auch bei abgebautem Screen)
6 Controller- und Widget-Tests Resend (Erfolg, emailSendLimited, Zurücksetzen bei neuem Versuch)
7 Widget-Test mit festen Texten de/en (Text + Semantics-Label), Banner für Drosselung/Offline/Unexpected
Sicherheit from-Tests inkl. Open-Redirect-Varianten (//, /\, %2F%2F, Steuerzeichen); toString() ohne Passwort/E-Mail
Querschnitt a11y-Guidelines hell/dunkel, textScaler 2.0 bei 320 px, Fokus auf erstes ungültiges Feld, Doppel-Submit

390 Tests grün.

Definition of Done

  • dart format, flutter analyze ohne Befund, flutter test grün
  • l10n DE/EN, keine hartkodierten Strings
  • Barrierefreiheit: Live-Region, Touch-Ziele, Kontrast als Guideline-Tests
  • Doku: docs/design/auth-screens.md
  • Android/iOS nicht gebaut (nur Web/Widget-Tests)
  • Nicht im echten Browser/Screenreader geprüft

Review

code-reviewer fand 1 Blocker (Krit.-7-Test ohne festen Text) und mehrere wichtige Punkte; alle behoben (57305da): feste Golden-Strings, toString() von LoginState/RegisterState maskiert Zugangsdaten, isRetryable zentral, finishAutofillContext auch bei abgebautem Screen, zusätzliche Tests. Dabei wurde safeFromPath gehärtet (%2F%2F, Steuerzeichen).

Hinweise

  • /forgot-password und /reset-password bleiben Platzhalter bis 01c.
  • Nicht umgesetzt (Folge in 01c): from-Weitergabe an die Links „Konto erstellen“/„Passwort vergessen“; Resend-Aktion wird beim Senden ausgeblendet statt deaktiviert.
  • Zusätzlich enthalten: chore(agents) – docs-writer darf den angebundenen Outline-MCP nutzen (.claude/agents/docs-writer.md).
  • Der Login-Client trimmt die E-Mail, wandelt sie aber nicht in Kleinbuchstaben um.

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/login, Mails: http://127.0.0.1:54324 (Mailpit)

🤖 Generated with Claude Code

## Zusammenfassung Anmeldung für #1 (KTO-01, PR 01b): `LoginScreen` mit Controller und State, allgemeiner Fehlertext, Hinweis bei unbestätigtem Konto mit erneutem Senden, Drosselungs- und Offline-Meldung, Autofill, sichere `from`-Weiterleitung. Passwort-Reset (01c) folgt; dieser PR schließt #1 nicht. Refs #1 Spec: `docs/specs/1-auth-registrierung-anmeldung.md` ## Akzeptanzkriterien (Anteil 01b) - [x] **5** Angemeldet bleiben: UI und Weiterleitung (`from`); dauerhafte Sitzung wird per E2E (T3) geprüft - [x] **6** Unbestätigtes Konto: Hinweis + „Bestätigungs-E-Mail erneut senden“; `emailSendLimited` zeigt Drosselungshinweis (F12) - [x] **7** Falsche E-Mail und falsches Passwort zeigen denselben Text (nur Banner, nie am Feld); 429 zeigt Drosselungshinweis. Echte Drosselung (Login #31) folgt in T3/D2 - [ ] **8, 9** Passwort zurücksetzen (01c) ## Abdeckungsmatrix | AK | Test(s) | |---|---| | 5 | `login_controller_test` (Erfolg, `from`), `login_screen_test` (Weiterleitung, Autofill-Abschluss auch bei abgebautem Screen) | | 6 | Controller- und Widget-Tests Resend (Erfolg, `emailSendLimited`, Zurücksetzen bei neuem Versuch) | | 7 | Widget-Test mit festen Texten de/en (Text + Semantics-Label), Banner für Drosselung/Offline/Unexpected | | Sicherheit | `from`-Tests inkl. Open-Redirect-Varianten (`//`, `/\`, `%2F%2F`, Steuerzeichen); `toString()` ohne Passwort/E-Mail | | Querschnitt | a11y-Guidelines hell/dunkel, textScaler 2.0 bei 320 px, Fokus auf erstes ungültiges Feld, Doppel-Submit | 390 Tests grün. ## 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, Touch-Ziele, Kontrast als Guideline-Tests - [x] Doku: `docs/design/auth-screens.md` - [ ] Android/iOS nicht gebaut (nur Web/Widget-Tests) - [ ] Nicht im echten Browser/Screenreader geprüft ## Review `code-reviewer` fand 1 Blocker (Krit.-7-Test ohne festen Text) und mehrere wichtige Punkte; alle behoben (`57305da`): feste Golden-Strings, `toString()` von `LoginState`/`RegisterState` maskiert Zugangsdaten, `isRetryable` zentral, `finishAutofillContext` auch bei abgebautem Screen, zusätzliche Tests. Dabei wurde `safeFromPath` gehärtet (`%2F%2F`, Steuerzeichen). ## Hinweise - `/forgot-password` und `/reset-password` bleiben Platzhalter bis 01c. - Nicht umgesetzt (Folge in 01c): `from`-Weitergabe an die Links „Konto erstellen“/„Passwort vergessen“; Resend-Aktion wird beim Senden ausgeblendet statt deaktiviert. - Zusätzlich enthalten: `chore(agents)` – docs-writer darf den angebundenen Outline-MCP nutzen (`.claude/agents/docs-writer.md`). - Der Login-Client trimmt die E-Mail, wandelt sie aber nicht in Kleinbuchstaben um. ## 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/login, Mails: http://127.0.0.1:54324 (Mailpit) ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
cschlaefke added 3 commits 2026-11-03 08:10:46 +00:00
KTO-01 Kriterien 5, 6 und 7 (01b): LoginController/State/Screen replace the
/login placeholder. Generic failure text for wrong email and password,
resend via ResendController (ResendOrigin.login), throttle and offline
banners, autofill hints, double-submit guard, validated from redirect.

Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fixed-wording tests for the generic credentials error, redacted toString for
login/register state, shared AuthFailure.isRetryable, autofill context
finished even when the router removes the screen, stricter from validation
(encoded and control-character variants), more widget tests.

Refs #1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
chore(agents): allow docs-writer to use the connected Outline MCP
All checks were successful
CI / flutter (pull_request) Successful in 1m10s
CI / web-build (pull_request) Successful in 53s
CI / supabase (pull_request) Successful in 3s
CI / supabase-tests (pull_request) Successful in 29s
bb63a9cb61
Refs #1

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

Review- und CI-Ergebnis (01b Anmeldung)

CI (Commit bb63a9c): alle 4 Jobs grün – flutter (1m10s), web-build (53s), supabase (3s), supabase-tests (29s).

Code-Review (Agent code-reviewer): 1 Blocker und mehrere wichtige Punkte, alle in 57305da behoben:

  • Krit.-7-Test prüft jetzt feste Texte (de/en, Text und Semantics-Label) statt nur den Localizations-Getter.
  • toString() von LoginState/RegisterState maskiert Zugangsdaten.
  • isRetryable zentral statt doppelt im Widget.
  • finishAutofillContext läuft auch, wenn der Router den Screen vorher abbaut (inkl. Test).
  • Zusätzliche Widget-Tests (Unexpected-Banner, Fokus, Kontrast des Fehler-Banners).
  • Zusatzfund: safeFromPath ließ /%2F%2Fevil.example und Pfade mit Steuerzeichen durch – gehärtet und getestet.

Bitte beachten:

  • /forgot-password und /reset-password bleiben Platzhalter bis 01c.
  • Echte Drosselung (Login #31 → 429) und dauerhafte Sitzung (Krit. 5) prüft erst der E2E-Test (T3/D2).
  • Nicht getestet: Android/iOS, echter Browser und Screenreader.
  • Folge für 01c: from-Weitergabe an die Links, Resend-Aktion deaktivieren statt ausblenden.

Merge durch Chris.

## Review- und CI-Ergebnis (01b Anmeldung) **CI (Commit `bb63a9c`):** alle 4 Jobs grün – `flutter` (1m10s), `web-build` (53s), `supabase` (3s), `supabase-tests` (29s). **Code-Review (Agent `code-reviewer`):** 1 Blocker und mehrere wichtige Punkte, alle in `57305da` behoben: - Krit.-7-Test prüft jetzt feste Texte (de/en, Text und Semantics-Label) statt nur den Localizations-Getter. - `toString()` von `LoginState`/`RegisterState` maskiert Zugangsdaten. - `isRetryable` zentral statt doppelt im Widget. - `finishAutofillContext` läuft auch, wenn der Router den Screen vorher abbaut (inkl. Test). - Zusätzliche Widget-Tests (Unexpected-Banner, Fokus, Kontrast des Fehler-Banners). - Zusatzfund: `safeFromPath` ließ `/%2F%2Fevil.example` und Pfade mit Steuerzeichen durch – gehärtet und getestet. **Bitte beachten:** - `/forgot-password` und `/reset-password` bleiben Platzhalter bis 01c. - Echte Drosselung (Login #31 → 429) und dauerhafte Sitzung (Krit. 5) prüft erst der E2E-Test (T3/D2). - Nicht getestet: Android/iOS, echter Browser und Screenreader. - Folge für 01c: `from`-Weitergabe an die Links, Resend-Aktion deaktivieren statt ausblenden. Merge durch Chris.
cschlaefke merged commit 86bcd25f30 into main 2026-11-03 08:33:36 +00:00
cschlaefke deleted branch feature/1-01b-anmeldung 2026-11-03 08:33:36 +00:00
Sign in to join this conversation.
No description provided.