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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user