fix(auth): address PR #758 round-7 medium/minor review

- Gate browser session creation on a successful refresh token. When the
  IdP returns no refresh token, SessionAuthBackend would silently reject
  every subsequent request and bounce the user back to /oauth/login in a
  loop. The callback now bails with a 400 + correlation ID + actionable
  hint about offline_access *before* writing browser_sessions or setting
  the cookie. Pinned by a new end-to-end unit test.
- Evict orphaned browser_sessions rows in SessionAuthBackend when the
  associated refresh token is gone, instead of letting them accumulate
  until TTL cleanup. Best-effort; deletion errors stay non-fatal.
- Demote identity-bearing logs in the Flow 2 OAuth callback (user_id,
  scopes, audience, expires_at) from INFO to DEBUG so they don't leak
  into multi-tenant log aggregation on every provision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-03 14:21:12 +02:00
co-authored by Claude Opus 4.7
parent 27fcf05d3a
commit b875eaf069
5 changed files with 221 additions and 35 deletions
@@ -101,6 +101,17 @@ class SessionAuthBackend(AuthenticationBackend):
session_id[:8],
user_id,
)
# Proactively evict the orphan so the table doesn't accumulate
# rows that the auth check will keep rejecting until TTL
# cleanup (PR #758 round-7 minor).
try:
await storage.delete_browser_session(session_id)
except Exception as e:
logger.warning(
"Failed to delete orphaned browser session %s…: %s",
session_id[:8],
e,
)
return None
return AuthCredentials(["authenticated"]), SimpleUser(user_id)