fix(auth): address PR #758 round-7 important review

- Coerce refresh_expires_in to int before arithmetic in both callback
  paths so IdPs that serialize the field as a JSON string (e.g. AWS
  Cognito) don't trigger an unhandled TypeError 500.
- Drop the orphaned oauth_session row written by _check_logged_in. The
  canonical Flow 2 row is created by generate_oauth_url_for_flow2 keyed
  by `state`, which is what the unified callback looks up; the
  flow2_<hex> session_id was never matched and just churned the table
  for 10 minutes per call.
- Match delete_cookie attributes (httponly, secure, samesite) to the
  set_cookie call on logout so browsers reliably evict the cookie even
  on implementations that consider security flags during deletion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-03 13:36:36 +02:00
co-authored by Claude Opus 4.7
parent ec9b9b2a75
commit 27fcf05d3a
3 changed files with 19 additions and 16 deletions
@@ -554,7 +554,9 @@ async def oauth_login_callback(request: Request) -> RedirectResponse | HTMLRespo
refresh_expires_in = token_data.get("refresh_expires_in")
refresh_expires_at = None
if refresh_expires_in:
refresh_expires_at = int(time.time()) + refresh_expires_in
# Some IdPs (e.g. AWS Cognito) return refresh_expires_in as a JSON
# string rather than an int; coerce to be safe.
refresh_expires_at = int(time.time()) + int(refresh_expires_in)
logger.debug(
"Refresh token expires in %ss (at timestamp %s)",
refresh_expires_in,
@@ -714,7 +716,15 @@ async def oauth_logout(request: Request) -> RedirectResponse | JSONResponse:
)
response = RedirectResponse(next_url, status_code=302)
response.delete_cookie("mcp_session")
# Match the attributes from set_cookie so browsers reliably evict the
# cookie even on edge-case implementations that consider security flags
# when matching for deletion.
response.delete_cookie(
"mcp_session",
httponly=True,
secure=_should_use_secure_cookies(),
samesite="lax",
)
logger.info("User logged out, session cookie cleared")
return response
+3 -1
View File
@@ -699,7 +699,9 @@ async def oauth_callback_nextcloud(request: Request):
refresh_expires_in = token_data.get("refresh_expires_in")
refresh_expires_at = None
if refresh_expires_in:
refresh_expires_at = int(time.time()) + refresh_expires_in
# Some IdPs (e.g. AWS Cognito) return refresh_expires_in as a JSON
# string rather than an int; coerce to be safe.
refresh_expires_at = int(time.time()) + int(refresh_expires_in)
logger.info(" refresh_expires_in: %ss", refresh_expires_in)
logger.info(" refresh_expires_at: %s", refresh_expires_at)