diff --git a/index.html b/index.html
index 719dc11..3dd8da4 100644
--- a/index.html
+++ b/index.html
@@ -19,9 +19,8 @@
{% endif %}
{% else %}
-
The collection mailbox is not connected yet. In a private window: open this page, sign in with
- your own account, click “Connect mailbox”, then sign in as
- {{ email_address }} (technical account).
+ The collection mailbox is not connected yet. Open /connect of this site in a private
+ window and sign in as {{ email_address }} (technical account).
diff --git a/oauth.py b/oauth.py
index 670a192..6f5f4a8 100644
--- a/oauth.py
+++ b/oauth.py
@@ -21,6 +21,7 @@ from cryptography.fernet import Fernet, InvalidToken
SCOPES = "openid profile email offline_access"
REFRESH_MARGIN_SEC = 60
PENDING_TTL_SEC = 600
+MAX_PENDING = 20 # /connect is reachable without login: bound the open requests
class OAuthError(Exception):
@@ -108,6 +109,8 @@ class MailboxOAuth:
now = self.clock()
with self._lock:
self._pending = {k: v for k, v in self._pending.items() if now - v[1] < PENDING_TTL_SEC}
+ while len(self._pending) >= MAX_PENDING:
+ del self._pending[min(self._pending, key=lambda k: self._pending[k][1])]
self._pending[state] = (verifier, now)
query = {
"response_type": "code",
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index d00212f..ff8c0a9 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -166,3 +166,10 @@ def test_callback_error_is_shown(monkeypatch):
monkeypatch.setattr(unsub, "OAUTH", FakeOAuth())
r = unsub.app.test_client().get("/oidc/callback?state=s&code=c")
assert r.status_code == 400 and "expected unsubscribe-bot" in r.get_data(as_text=True)
+
+
+def test_pending_requests_are_bounded(tmp_path):
+ mb = _mb(tmp_path, [])
+ for _ in range(oauth.MAX_PENDING + 15):
+ mb.start()
+ assert len(mb._pending) == oauth.MAX_PENDING