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 contextlib import asynccontextmanager
from urllib.parse import quote from urllib.parse import quote
import httpx
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse
@@ -72,8 +73,43 @@ def create_app() -> FastAPI:
): ):
return await call_next(request) 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): 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) return await call_next(request)
# Check cooldown — don't re-check within 5 minutes # Check cooldown — don't re-check within 5 minutes