fix: stille Anmeldeschleife bei Secure-Cookie über HTTP erklären
CI / backend (push) Successful in 2m33s
Images bauen / build (backend) (push) Successful in 3m45s
Images bauen / build (frontend) (push) Successful in 4m23s
CI / frontend (push) Successful in 6m38s

Bei COOKIE_SECURE=true und Zugriff über HTTP verwirft der Browser das
Sitzungs-Cookie. Die Anmeldung selbst meldet Erfolg, die nächste Anfrage gilt
aber als nicht angemeldet – der Nutzer landet ohne jede Meldung wieder auf der
Anmeldemaske und hält es für ein falsches Passwort.

- Das Frontend prüft die Sitzung unmittelbar nach der Anmeldung und nennt bei
  einem Fehlschlag die Ursache samt beider Auswege
- Das Backend protokolliert dieselbe Kombination als Warnung und wertet dabei
  X-Forwarded-Proto aus, weil hinter einem Reverse Proxy immer http ankommt
- docs/deployment.md beschreibt das Symptom wörtlich

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
Jonas Menzel
2026-09-10 00:00:29 +02:00
co-authored by Claude Opus 5
parent c110340628
commit 998d5867df
6 changed files with 215 additions and 9 deletions
+36 -1
View File
@@ -1,11 +1,14 @@
"""Setzen und Löschen der Authentifizierungs-Cookies."""
import logging
from datetime import UTC, datetime
from fastapi import Response
from fastapi import Request, Response
from app.core.config import settings
logger = logging.getLogger(__name__)
ACCESS_COOKIE = "moneyfy_access"
REFRESH_COOKIE = "moneyfy_refresh"
@@ -13,6 +16,38 @@ REFRESH_COOKIE = "moneyfy_refresh"
REFRESH_COOKIE_PATH = "/api/auth"
def request_is_secure(request: Request) -> bool:
"""Kam die Anfrage über HTTPS?
Hinter einem Reverse Proxy steht das echte Schema in `X-Forwarded-Proto`;
das Schema der Verbindung zum Container ist dort immer `http`.
"""
weitergeleitet = request.headers.get("x-forwarded-proto", "")
if weitergeleitet:
# Bei mehreren Proxies steht das ursprüngliche Schema vorne.
return weitergeleitet.split(",")[0].strip().lower() == "https"
return request.url.scheme == "https"
def warn_if_cookie_unusable(request: Request) -> bool:
"""Meldet die Kombination, bei der der Browser das Cookie verwirft.
`Secure`-Cookies über eine reine HTTP-Verbindung werden stillschweigend
weggeworfen: Die Anmeldung meldet Erfolg, die nächste Anfrage gilt aber
wieder als nicht angemeldet. Ohne diesen Hinweis ist das kaum zu erkennen.
"""
if not settings.cookie_secure or request_is_secure(request):
return False
logger.warning(
"Anmeldung über %s ohne HTTPS bei COOKIE_SECURE=true der Browser "
"verwirft das Sitzungs-Cookie und die Anmeldemaske erscheint erneut. "
"Entweder über HTTPS zugreifen oder COOKIE_SECURE=false setzen.",
request.url.hostname or "unbekannt",
)
return True
def set_auth_cookies(
response: Response,
access_token: str,