fix(auth): address PR #758 round-4 review
Seven findings from the latest review on #758 (3 medium, 4 low/nit): Medium: - storage.py: replace 5 ``assert self.cipher is not None`` sites with explicit ``RuntimeError`` so missing TOKEN_ENCRYPTION_KEY can't silently become an AttributeError under ``python -O`` - session_backend.py: document the silent-invalidation invariant — refresh-token TTL expiry without explicit logout deliberately makes the browser session unusable; future readers must not relax it - server/oauth_tools.py: drop user_id from the Flow 2 session_id identifier — use ``flow2_{secrets.token_hex(16)}`` so audit logs and DB rows don't carry user_id in the session_id field Low / nit: - token_utils.py: drop _fetch_locks dict entry in finally so a probed deployment can't grow the lock dict without bound; coalescing test now pins the invariant with len(_fetch_locks) == 0 - browser_oauth_routes.py: strip trailing slash from settings.nextcloud_host before constructing the well-known URL so a host configured as ``https://cloud.example.com/`` doesn't produce a double-slash - browser_oauth_routes.py: add comment explaining the three-layer CSRF policy on the mcp_session cookie set (SameSite=Lax + POST-only logout + Origin/Referer check) - oauth_routes.py: convert all 23 f-string log calls to lazy %-style per the CLAUDE.md / memory feedback_lazy_logging convention Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
3a4fa8adc8
commit
b696541918
@@ -75,20 +75,31 @@ async def _get_cached(
|
||||
if entry is not None and time.time() < entry[0]:
|
||||
return entry[1]
|
||||
lock = await _get_fetch_lock(url)
|
||||
async with lock:
|
||||
# Re-check inside the lock — a concurrent waiter may have already
|
||||
# populated the cache before we acquired it.
|
||||
entry = cache.get(url)
|
||||
if entry is not None and time.time() < entry[0]:
|
||||
return entry[1]
|
||||
async with nextcloud_httpx_client(
|
||||
follow_redirects=follow_redirects
|
||||
) as http_client:
|
||||
response = await http_client.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
cache[url] = (time.time() + _OIDC_CACHE_TTL, data)
|
||||
return data
|
||||
try:
|
||||
async with lock:
|
||||
# Re-check inside the lock — a concurrent waiter may have already
|
||||
# populated the cache before we acquired it.
|
||||
entry = cache.get(url)
|
||||
if entry is not None and time.time() < entry[0]:
|
||||
return entry[1]
|
||||
async with nextcloud_httpx_client(
|
||||
follow_redirects=follow_redirects
|
||||
) as http_client:
|
||||
response = await http_client.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
cache[url] = (time.time() + _OIDC_CACHE_TTL, data)
|
||||
return data
|
||||
finally:
|
||||
# Drop the dict entry so a misconfigured deployment hitting
|
||||
# arbitrary URLs can't grow ``_fetch_locks`` without bound (PR #758
|
||||
# round-4 review nit 4). Already-queued waiters share our local
|
||||
# ``lock`` reference and remain coalesced; new arrivals lazily
|
||||
# recreate a lock — by which time the cache is populated, so they
|
||||
# short-circuit before reaching the lock anyway.
|
||||
async with _fetch_locks_lock:
|
||||
if _fetch_locks.get(url) is lock:
|
||||
del _fetch_locks[url]
|
||||
|
||||
|
||||
async def get_oidc_discovery(discovery_url: str) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user