Fix: Navidrome-Login — Passwort-Sichtbarkeit + Trim (Login fehlgeschlagen)

Systematische Diagnose bewies: App-Code, Server, User 'Baka' und Passwort sind
korrekt (1:1-Nachbau des App-Requests inkl. Salt/Token -> Navidrome status:ok).
Der Fehler lag im auf dem Gerät verwendeten Credential-Wert:
- der alte Build (Ping-Fehlalarm) persistierte ungültige Zugangsdaten,
- verdeckte Tippfehler im obscureText-Passwortfeld blieben unsichtbar.

Fix:
- Passwort-Anzeigen-Umschalter (Auge-Icon) im Login-Dialog.
- URL + Passwort werden getrimmt (Autofill/Tastatur-Leerzeichen).
Ein erfolgreicher Login ueberschreibt die alten gespeicherten Credentials;
der Ping-Fix stellt sicher, dass nur gueltige gespeichert werden.

84/84 Tests gruen, analyze sauber.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tr2UwurtxyJUpWpBSeVmBm
This commit is contained in:
Hermes (Server)
2026-08-20 09:52:07 +02:00
co-authored by Claude Opus 4.8
parent 2e40cc2024
commit 3d1e00646a
2 changed files with 37 additions and 8 deletions
+16
View File
@@ -5,6 +5,22 @@ Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
## [Unreleased] ## [Unreleased]
### 🔎 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
App-Requests inkl. Salt/Token) hat bewiesen: **App-Code, Server, User „Baka"
und Passwort sind korrekt** — der Fehler lag im auf dem Gerät verwendeten
Credential-Wert (der ältere Build mit dem Ping-Fehlalarm persistierte
ungültige Zugangsdaten; verdeckte Tippfehler im Passwortfeld blieben
unsichtbar).
- 🔧 **Passwort-Anzeigen-Umschalter** (Auge-Icon) im Navidrome-Login — Tippfehler
werden sichtbar.
- 🔧 **URL + Passwort werden getrimmt** (randständige Leerzeichen aus Autofill /
Tastatur entfernt).
- ️ Ein erfolgreicher Login überschreibt die alten (ungültigen) gespeicherten
Zugangsdaten — der Ping-Fix aus dieser Version stellt sicher, dass künftig
nur noch gültige Credentials gespeichert werden.
### 🔒 Datenschutz: Log-Bereinigung vor Server-Upload (2026-08-20) ### 🔒 Datenschutz: Log-Bereinigung vor Server-Upload (2026-08-20)
-**Was:** Diagnose-Logs werden jetzt zentral bereinigt, bevor sie an -**Was:** Diagnose-Logs werden jetzt zentral bereinigt, bevor sie an
`logs.baka-net.de` gehen. Der lokale `debugPrint` bleibt vollständig. `logs.baka-net.de` gehen. Der lokale `debugPrint` bleibt vollständig.
+21 -8
View File
@@ -160,6 +160,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
final passCtrl = TextEditingController(); final passCtrl = TextEditingController();
String selectedUser = 'Dustin'; String selectedUser = 'Dustin';
bool verbindet = false; bool verbindet = false;
bool obscure = true;
String? fehler; String? fehler;
showDialog( showDialog(
@@ -228,11 +229,20 @@ class _SettingsScreenState extends State<SettingsScreen> {
TextField( TextField(
controller: passCtrl, controller: passCtrl,
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
obscureText: true, obscureText: obscure,
decoration: const InputDecoration( decoration: InputDecoration(
labelText: 'Passwort', labelText: 'Passwort',
labelStyle: TextStyle(color: Colors.grey), labelStyle: const TextStyle(color: Colors.grey),
border: OutlineInputBorder(), border: const OutlineInputBorder(),
// Anzeigen-Umschalter: verdeckte Tippfehler im Passwort
// sichtbar machen (häufigste Login-Fehlerquelle).
suffixIcon: IconButton(
icon: Icon(
obscure ? Icons.visibility_off : Icons.visibility,
color: Colors.grey,
),
onPressed: () => setDialogState(() => obscure = !obscure),
),
), ),
), ),
if (fehler != null) ...[ if (fehler != null) ...[
@@ -262,16 +272,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
? null ? null
: () async { : () async {
setDialogState(() => verbindet = true); setDialogState(() => verbindet = true);
// Randständige Leerzeichen entfernen (verdeckte
// Fehlerquelle im obscureText-Feld / bei Autofill).
final url = urlCtrl.text.trim();
final pass = passCtrl.text.trim();
logger.info( logger.info(
'Navidrome Login-Versuch (pwLeer=${passCtrl.text.isEmpty})'); 'Navidrome Login-Versuch (pwLeer=${pass.isEmpty})');
_navidrome.setCredentials( _navidrome.setCredentials(url, selectedUser, pass);
urlCtrl.text, selectedUser, passCtrl.text);
final ok = await _navidrome.ping(); final ok = await _navidrome.ping();
setDialogState(() => verbindet = false); setDialogState(() => verbindet = false);
if (ok && ctx.mounted) { if (ok && ctx.mounted) {
logger.info('Navidrome Login erfolgreich'); logger.info('Navidrome Login erfolgreich');
await _navidrome.speichereZugangsdaten( await _navidrome.speichereZugangsdaten(
urlCtrl.text, selectedUser, passCtrl.text); url, selectedUser, pass);
if (ctx.mounted) Navigator.pop(ctx); if (ctx.mounted) Navigator.pop(ctx);
if (mounted) setState(() {}); if (mounted) setState(() {});
} else if (ctx.mounted) { } else if (ctx.mounted) {