L2: verify auth state with account on each request
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m49s

When user has artdag_session cookie, periodically (every 30s) check
account's /auth/internal/check-device endpoint. If account says the
device is no longer active (SSO logout), clear the cookie immediately.
Prevents stale sign-in after logging out from another app.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-25 01:42:09 +00:00
parent 0e14d2761a
commit f1d80a1777

View File

@@ -10,6 +10,7 @@ from pathlib import Path
from contextlib import asynccontextmanager
from urllib.parse import quote
import httpx
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse
@@ -72,8 +73,43 @@ def create_app() -> FastAPI:
):
return await call_next(request)
# Already logged in — pass through
# Already logged in — verify account hasn't revoked auth
if get_user_from_cookie(request):
device_id = getattr(request.state, "device_id", None)
if device_id:
# Check every 30s whether account still considers this device active
check_at = request.cookies.get("auth_check_at")
now = time.time()
stale = True
if check_at:
try:
stale = (now - float(check_at)) > 30
except (ValueError, TypeError):
pass
if stale and settings.internal_account_url:
try:
async with httpx.AsyncClient(timeout=3) as client:
resp = await client.get(
f"{settings.internal_account_url.rstrip('/')}"
f"/auth/internal/check-device"
f"?device_id={device_id}&app=artdag_l2"
)
if resp.status_code == 200 and not resp.json().get("active"):
# Account revoked — clear cookie
response = await call_next(request)
response.delete_cookie("artdag_session")
response.delete_cookie("pnone_at")
response.delete_cookie("auth_check_at")
return response
except Exception:
pass
# Update check timestamp
response = await call_next(request)
response.set_cookie(
"auth_check_at", str(now), max_age=60,
httponly=True, samesite="lax", secure=True,
)
return response
return await call_next(request)
# Check cooldown — don't re-check within 5 minutes