Summary
When /api/webrtc/ws rejects a missing, invalid, or expired
authSig, WebSocketView raises HTTPUnauthorized (401).
Home Assistant's HTTP ban middleware treats any raised
HTTPUnauthorized as a failed login
(homeassistant/components/http/ban.py → process_wrong_login),
regardless of requires_auth = False on the view.
With ip_ban_enabled / a positive login_attempts_threshold,
repeated expired-ticket reconnects (e.g. a sleeping browser tab
with a WebRTC card open) can ban the client IP. Behind a reverse
proxy with use_x_forwarded_for, that can be the household WAN
address and lock out all external access.
This is not a real login failure (no password/token for a user
account). It is a short-lived stream signature check failing.
Evidence
Component code (current):
# WebSocketView — requires_auth = False
elif not utils.validate_signed_request(request):
raise HTTPUnauthorized() # → HA ban counter
HA core only increments the ban counter on HTTPUnauthorized,
not on HTTPForbidden (403). HA itself uses 403 for already-banned
IPs in that same middleware.
Observed in the wild (HA 2025.x / WebRTC v3.6.1):
- Card calls
auth/sign_path for /api/webrtc/ws
(default expires=30 seconds).
- Sleeping/throttled Firefox tab presents the ticket ~60–110s
later → JWT expired → 401 → "Login attempt or request with
invalid authentication" against /api/webrtc/ws?...&url=....
- After
login_attempts_threshold (e.g. 5) hits, IP is written
to ip_bans.yaml.
Related historical symptom (different root cause, same ban
fallout): #318 / #321 / #320 — broken signing after HA 2022.7
also produced IP bans via the same 401 path. Signing was fixed;
using 401 for ticket rejection was not.
Tracked internally: https://issues.infotune.com/tasks/476
Expected behavior
Rejecting an invalid/expired stream ticket should not
increment HA's failed-login / IP-ban counter.
Suggested fix: raise HTTPForbidden (403) instead of
HTTPUnauthorized (401) when validate_signed_request fails
(and similarly anywhere else a stream signature/cookie check
uses 401 for the same reason, e.g. HLS cookie check if it has
the same ban side effect).
Connection is still denied; real login brute-force protection
is unchanged.
Optional card-side hardening (separate)
Also consider passing a longer expires to auth/sign_path
and/or ensuring reconnect never opens a WebSocket with a stale
signed URL. That reduces expiry races but does not remove
the ban footgun while 401 remains.
Environment
- custom component: AlexxIT/WebRTC (e.g. v3.6.1)
- Home Assistant with
login_attempts_threshold ≥ 1
- Often: reverse proxy +
use_x_forwarded_for
Summary
When
/api/webrtc/wsrejects a missing, invalid, or expiredauthSig,WebSocketViewraisesHTTPUnauthorized(401).Home Assistant's HTTP ban middleware treats any raised
HTTPUnauthorizedas a failed login(
homeassistant/components/http/ban.py→process_wrong_login),regardless of
requires_auth = Falseon the view.With
ip_ban_enabled/ a positivelogin_attempts_threshold,repeated expired-ticket reconnects (e.g. a sleeping browser tab
with a WebRTC card open) can ban the client IP. Behind a reverse
proxy with
use_x_forwarded_for, that can be the household WANaddress and lock out all external access.
This is not a real login failure (no password/token for a user
account). It is a short-lived stream signature check failing.
Evidence
Component code (current):
HA core only increments the ban counter on
HTTPUnauthorized,not on
HTTPForbidden(403). HA itself uses 403 for already-bannedIPs in that same middleware.
Observed in the wild (HA 2025.x / WebRTC v3.6.1):
auth/sign_pathfor/api/webrtc/ws(default
expires=30seconds).later → JWT expired → 401 → "Login attempt or request with
invalid authentication" against
/api/webrtc/ws?...&url=....login_attempts_threshold(e.g. 5) hits, IP is writtento
ip_bans.yaml.Related historical symptom (different root cause, same ban
fallout): #318 / #321 / #320 — broken signing after HA 2022.7
also produced IP bans via the same 401 path. Signing was fixed;
using 401 for ticket rejection was not.
Tracked internally: https://issues.infotune.com/tasks/476
Expected behavior
Rejecting an invalid/expired stream ticket should not
increment HA's failed-login / IP-ban counter.
Suggested fix: raise
HTTPForbidden(403) instead ofHTTPUnauthorized(401) whenvalidate_signed_requestfails(and similarly anywhere else a stream signature/cookie check
uses 401 for the same reason, e.g. HLS cookie check if it has
the same ban side effect).
Connection is still denied; real login brute-force protection
is unchanged.
Optional card-side hardening (separate)
Also consider passing a longer
expirestoauth/sign_pathand/or ensuring reconnect never opens a WebSocket with a stale
signed URL. That reduces expiry races but does not remove
the ban footgun while 401 remains.
Environment
login_attempts_threshold≥ 1use_x_forwarded_for