diff --git a/CHANGELOG.md b/CHANGELOG.md index cdab9c05..b68cb121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to - 🐛(frontend) fix hand icon and queue position alignment and position #1119 - 🩹(backend) add page_size to pagination for room endpoints #1131 +- 🐛(backend) refactor lobby throttling using participant id instead of IP #1129 ## [1.10.0] - 2026-03-05 diff --git a/src/backend/core/api/throttling.py b/src/backend/core/api/throttling.py index b6ec0c72..ecd4304b 100644 --- a/src/backend/core/api/throttling.py +++ b/src/backend/core/api/throttling.py @@ -1,5 +1,7 @@ """Throttling modules for the API.""" +from django.conf import settings + from lasuite.drf.throttling import MonitoredThrottleMixin from rest_framework.throttling import AnonRateThrottle from sentry_sdk import capture_message @@ -19,6 +21,35 @@ class RequestEntryAnonRateThrottle(MonitoredAnonRateThrottle): scope = "request_entry" + def get_cache_key(self, request, view): + """Use the lobby participant cookie ID as the throttle cache key. + + Only throttle if a cookie is already set. If no cookie exists yet, + return None to skip throttling — the cookie will be set on the first + response, and throttling will apply from the second request onward. + + Keying on the cookie rather than the IP address prevents penalising + multiple users behind the same NAT/proxy, and is consistent with how + LobbyService identifies participants. + + Note: as per DRF documentation, application-level throttling is not a + security measure against brute-force or DoS attacks. This throttle exists + solely to guard against accidental hammering from buggy clients. + """ + + if request.user and request.user.is_authenticated: + return None # Only throttle unauthenticated requests. + + participant_id = request.COOKIES.get(settings.LOBBY_COOKIE_NAME) + + if participant_id is None: + return None # No throttling for cookieless requests + + return self.cache_format % { + "scope": self.scope, + "ident": participant_id, + } + class CreationCallbackAnonRateThrottle(MonitoredAnonRateThrottle): """Throttle Anonymous user requesting room generation callback"""