From 5d08da10861953f7575a0f32dcd17f22c020ca1a Mon Sep 17 00:00:00 2001 From: "Hermes (Server)" Date: Thu, 20 Aug 2026 16:13:34 +0200 Subject: [PATCH] Login-Dialog zeigt echte Server-Fehlermeldung + pwLen im Log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tiefe Debug-Runde bewiesen: Server, DB-Passwort und App-Token-Logik sind korrekt (8/8-Repro gegen Live-Server) — abgelehnt wird ein falscher Passwort-Wert vom Gerät. Damit das künftig sofort sichtbar ist: - NavidromeService.letzterLoginFehler hält die Server-Meldung fest - Login-Dialog zeigt sie statt generischem "Login fehlgeschlagen" - Fehler-Log enthält die Passwort-Länge (nie das Passwort selbst) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01JRj3pupcigUi683vy2Wwnz --- CHANGELOG.md | 18 ++++++++++ lib/services/navidrome_service.dart | 6 ++++ lib/settings/settings_screen.dart | 9 +++-- test/navidrome_login_fehler_test.dart | 48 +++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 test/navidrome_login_fehler_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e40828..0827414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/). ## [Unreleased] +### 🔎 Login-Dialog zeigt jetzt die echte Server-Fehlermeldung (2026-08-20) +- ✅ **Was:** Schlägt der Navidrome-Login fehl, zeigt der Dialog jetzt die + Original-Meldung des Servers (z. B. „Wrong username or password") statt + nur „Login fehlgeschlagen". So ist sofort klar, ob das Passwort falsch ist + oder ein Netzwerkproblem vorliegt. +- 🔧 **Details:** + - `NavidromeService.letzterLoginFehler` hält die Server-Meldung des letzten + abgelehnten Logins fest (wird bei jedem Ping zurückgesetzt). + - Das Fehler-Log enthält jetzt die Passwort-**Länge** (`pwLen=…`, nie das + Passwort selbst) — ein Tippfehler wird so im Log sofort sichtbar + (`Melo…!` hat 11 Zeichen). +- 🐛 **Hintergrund (tiefe Debug-Runde):** Server-seitig ist alles nachweislich + korrekt — Subsonic-API (`/rest/*`) läuft trotz SSO-Umbau ohne Forward-Auth, + DB-Passwort für „Baka" stimmt, und die exakte App-Token-Logik (Base64-Salt + + md5) wurde 8/8-mal erfolgreich gegen den Live-Server reproduziert. Die + Ablehnungen kommen von einem falschen Passwort-Wert auf dem Gerät; die neue + Fehlermeldung macht genau das künftig direkt sichtbar. + ### 🔎 Login-Diagnose: Passwort-Sichtbarkeit + Trim (2026-08-20) - 🐛 **„Login fehlgeschlagen" trotz korrekter Zugangsdaten:** Systematische Analyse (App-Code, App-Logs, Caddy/Navidrome-Logs, 1:1-Nachbau des diff --git a/lib/services/navidrome_service.dart b/lib/services/navidrome_service.dart index 5ca905d..dd5b1e0 100644 --- a/lib/services/navidrome_service.dart +++ b/lib/services/navidrome_service.dart @@ -77,6 +77,10 @@ class NavidromeService { String _salt = ''; String? _token; + /// Server-Meldung des letzten abgelehnten Logins (z. B. „Wrong username + /// or password"), damit der Login-Dialog die echte Ursache zeigen kann. + String? letzterLoginFehler; + static const _version = '1.16.1'; static const _client = 'Melo'; @@ -179,6 +183,7 @@ class NavidromeService { } Future ping() async { + letzterLoginFehler = null; try { final r = await http.get(_uri('ping.view')).timeout(const Duration(seconds: 10)); if (r.statusCode != 200) { @@ -191,6 +196,7 @@ class NavidromeService { logger.info('✅ Navidrome Verbindung erfolgreich: $_serverUrl'); return true; } on NavidromeException catch (e) { + letzterLoginFehler = e.message; logger.warning('Navidrome Login abgelehnt: ${e.message}'); return false; } catch (e) { diff --git a/lib/settings/settings_screen.dart b/lib/settings/settings_screen.dart index 1973143..1e56f35 100644 --- a/lib/settings/settings_screen.dart +++ b/lib/settings/settings_screen.dart @@ -288,10 +288,15 @@ class _SettingsScreenState extends State { if (ctx.mounted) Navigator.pop(ctx); if (mounted) setState(() {}); } else if (ctx.mounted) { + // Server-Meldung + Passwortlänge festhalten: „Wrong + // username or password" bei pwLen=10 zeigt sofort + // einen Tippfehler statt eines Netzwerkproblems. + final grund = _navidrome.letzterLoginFehler; await logger.error( - 'Navidrome Login fehlgeschlagen (pwLeer=${passCtrl.text.isEmpty})'); + 'Navidrome Login fehlgeschlagen ' + '(pwLen=${pass.length}, grund=${grund ?? 'unbekannt'})'); setDialogState(() => - fehler = '❌ Login fehlgeschlagen'); + fehler = '❌ ${grund ?? 'Login fehlgeschlagen'}'); } }, child: const Text('Login', diff --git a/test/navidrome_login_fehler_test.dart b/test/navidrome_login_fehler_test.dart new file mode 100644 index 0000000..d36744a --- /dev/null +++ b/test/navidrome_login_fehler_test.dart @@ -0,0 +1,48 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:melo/services/logger_service.dart'; +import 'package:melo/services/navidrome_service.dart'; + +/// Tests für die Login-Fehlerdiagnose: Lehnt der Server den Login ab +/// (status:failed, z. B. „Wrong username or password"), muss die App die +/// Server-Meldung festhalten, damit der Login-Dialog sie anzeigen kann — +/// ein generisches „Login fehlgeschlagen" verschleiert die Ursache. +void main() { + late HttpServer server; + + setUpAll(() async => logger.init()); + + Future starteServer(String body) async { + server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); + server.listen((req) { + req.response + ..statusCode = 200 + ..headers.contentType = ContentType.json + ..write(body); + req.response.close(); + }); + return Uri.parse('http://127.0.0.1:${server.port}'); + } + + tearDown(() async => server.close(force: true)); + + test('ping speichert Server-Meldung bei abgelehntem Login', () async { + final url = await starteServer( + '{"subsonic-response":{"status":"failed","version":"1.16.1",' + '"error":{"code":40,"message":"Wrong username or password"}}}'); + final svc = NavidromeService(); + svc.setCredentials(url.toString(), 'Baka', 'falsch'); + expect(await svc.ping(), isFalse); + expect(svc.letzterLoginFehler, 'Wrong username or password'); + }); + + test('ping löscht alte Fehlermeldung bei Erfolg', () async { + final url = await starteServer( + '{"subsonic-response":{"status":"ok","version":"1.16.1"}}'); + final svc = NavidromeService(); + svc.setCredentials(url.toString(), 'Baka', 'richtig'); + expect(await svc.ping(), isTrue); + expect(svc.letzterLoginFehler, isNull); + }); +}