fix: dual-key rate limiting for auth + websocket reconnect guard

- Pass client IP (X-Real-IP header) through Flask to daemon for both
  password login and WebAuthn authenticate-finish endpoints
- Rate limiter now checks both IP and username buckets: IP layer catches
  enumeration/brute-force attacks across multiple usernames; username
  layer protects against single-account targeting from multiple IPs
- Add _wsRefreshing flag to prevent double-scheduling reconnect when
  onclose fires during token refresh; simplify async IIFE to .then()/.catch()
- Reset _wsRefreshing on websocket onopen for safety
This commit is contained in:
2026-07-28 00:54:48 +00:00
parent 76cd219050
commit 358573567d
4 changed files with 30 additions and 20 deletions
+4 -2
View File
@@ -90,7 +90,8 @@ def auth_login(_request: Any, body: Any) -> dict[str, Any]:
if not username or not password:
raise ValueError("username and password are required")
if not check_login_rate(username):
client_ip = body.get("client_ip")
if not check_login_rate(username, client_ip):
raise ValueError("Too many login attempts. Please try again later.")
user = verify_user_password(username, password)
@@ -449,11 +450,12 @@ def webauthn_authenticate_finish(_request: Any, body: Any) -> dict[str, Any]:
username = body.get("username")
assertion_response = body.get("assertion_response")
auth_options = body.get("auth_options")
client_ip = body.get("client_ip")
if not username or not assertion_response or not auth_options:
raise ValueError("username, assertion_response, and auth_options are required")
if not check_webauthn_rate(username):
if not check_webauthn_rate(username, client_ip):
raise ValueError("Too many WebAuthn attempts. Please try again later.")
try: