Die Anmeldung galt fest 7 Tage - und die Frist lief ab dem *Anmelden*, nicht
ab der letzten Nutzung. Wer die App taeglich benutzte, flog trotzdem
punktgenau nach einer Woche hinaus.
Beides ist jetzt geregelt:
- Einstellungen -> Anmeldung: 7 Tage bis 1 Jahr oder unbegrenzt. Gilt fuer
Website und App gemeinsam, weil beide dasselbe Token bekommen - auch nach
Single Sign-on.
- POST /auth/refresh schiebt die Frist beim Start nach vorn. Damit zaehlt die
eingestellte Dauer ab dem letzten Besuch, und eine geaenderte Einstellung
greift ohne Neuanmeldung.
"Unbegrenzt" heisst ein Token ganz ohne exp. Das ist bewusst folgenreich: Es
gibt keine Sperrliste, eine erteilte Anmeldung laesst sich nicht einzeln
zurueckziehen. Die Oberflaeche sagt das dazu und nennt den einzigen Ausweg
(JWT_SECRET aendern).
/auth/refresh nimmt bewusst nur JWTs an, keine langlebigen API-Token: aus
einem jederzeit widerrufbaren Zugang (Home Assistant) waere sonst ein
unwiderruflicher geworden.
Der Rueckfallwert aus der Umgebung steigt von 7 auf 30 Tage - er gilt nur noch,
solange nichts eingestellt ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neben dem Passwort-Login gibt es jetzt einen zweiten, gleichberechtigten Weg:
OpenID Connect gegen einen selbst gehosteten Anmeldedienst. Ohne Konfiguration
aendert sich nichts - keine SSO-Knoepfe, Passwort-Login wie bisher.
Der Anmeldedienst ist NUR fuer die Anmeldung zustaendig. Danach stellt Vorrania
sein eigenes Token aus wie beim Passwort-Login, deshalb bleiben deps.py, die
Rollenpruefung und die langlebigen API-Tokens unveraendert.
Backend
- Anbieter kommen aus der Umgebung (OIDC_PROVIDERS + OIDC_<KUERZEL>_*). Ein
zweiter Anbieter ist damit reine Konfiguration, ohne Codeaenderung.
- Browser-Weg (vertraulicher Client) und App-Weg (oeffentlicher Client, PKCE)
enden beide im selben Token-Schema.
- state/nonce/PKCE-Verifier liegen in einer kurzlebigen Tabelle statt in einem
Cookie: Starlettes CORS-Schicht spiegelt bei allow_origins=["*"] zusammen mit
allow_credentials die Herkunft zurueck, sobald ein Request ein Cookie traegt.
Ohne Cookie entsteht diese Wechselwirkung erst gar nicht - und der Ablauf
bleibt mit der db-Fixture pruefbar.
- Zuordnung ueber die sub des Anbieters; die Erstverknuepfung laeuft ueber die
BESTAETIGTE E-Mail-Adresse. Rollen kommen nie aus dem Anbieter, und ein
gleichlautender preferred_username uebernimmt nie ein bestehendes Konto.
- users.password_hash darf jetzt leer sein (reine SSO-Konten). verify_password
faengt das ab, sonst antwortete der Login mit 500 statt 401.
Verknuepfen bestehender Konten
- Neue Seite "Mein Konto" (fuer alle Benutzer, nicht nur Admins): verknuepfen
und wieder loesen. Die Berechtigung stammt aus der laufenden Sitzung, es
braucht also weder eine hinterlegte Adresse noch Admin-Rechte.
- Der Start laeuft ueber POST, weil eine Seitennavigation das Bearer-Token
nicht mitschicken kann.
- Die letzte Anmeldemoeglichkeit eines Kontos laesst sich nicht loesen.
iOS
- AppAuth (erste Paketabhaengigkeit) mit PKCE im System-Browser, nie in einem
eingebetteten WebView, und ohne Client-Secret.
- offline_access nur im App-Weg: der Browser-Weg braucht kein Refresh-Token und
bekaeme sonst einen ueberfluessigen Zustimmungsdialog.
- Anmeldezustand je Serverprofil im Keychain; abgelaufene Token erneuert die App
still im Hintergrund. Gleichzeitige 401er teilen sich eine Erneuerung.
Deployment
- docker-compose reicht die ganze .env durch. Einzeln aufzuzaehlen ginge nur mit
fest verdrahteten Kuerzeln - genau das soll ein weiterer Anbieter nicht
erfordern.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Defekte, die zusammen das gemeldete Muster ergeben.
1) Es gab keine zentrale Behandlung von HTTP 401. AuthProvider prueft die
Anmeldung EINMAL beim Start; laeuft das Token danach ab, bleibt der Zustand
"angemeldet" stehen, waehrend jede Abfrage scheitert. Seiten mit
.catch(() => {}) (18 Stellen) zeigen dann schlicht nichts, andere eine
englische Servermeldung - genau das "mal gar nichts, mal die Haelfte".
Jetzt raeumt der API-Client bei 401 die Sitzung auf und der Login sagt, dass
sie abgelaufen ist. Beim Anmelden selbst greift das nicht, dort heisst 401
"Passwort falsch".
2) Der Start hatte ein blankes `catch`, das das Token bei JEDEM Fehler loeschte -
auch bei 502 oder weggebrochenem Netz. Wer waehrend eines Server-Neustarts F5
drueckte, war deshalb abgemeldet, obwohl die Sitzung voellig in Ordnung war.
Das duerfte der haeufigere Ausloeser gewesen sein, weil in letzter Zeit oft neu
deployt wurde. Jetzt wird nur bei 401/403 abgemeldet; bei allem anderen bleibt
die Anmeldung liegen und es erscheint "Server nicht erreichbar" mit einem
Knopf zum erneuten Versuchen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>