Remove sso_hint cookie, add sso-clear logout chain

sso_hint on .rose-ash.com was blocked by Safari ITP — the exact
problem we're solving. Replaced with redirect chain: account logout
chains through each client app's /auth/sso-clear to clear all
first-party sessions without any cross-domain cookies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-23 12:17:22 +00:00
parent 223491fad5
commit a93a456ac5
2 changed files with 10 additions and 37 deletions

View File

@@ -123,40 +123,6 @@ def create_base_app(
for fn in before_request_fns:
app.before_request(fn)
# Silent SSO: if account set sso_hint cookie, trigger OAuth once
if name != "account":
from urllib.parse import quote as _quote
@app.before_request
async def _sso_check():
from quart import session as qs
if request.path.startswith("/auth/"):
return
uid = qs.get("uid")
has_hint = request.cookies.get("sso_hint")
# SSO revoked (account logged out) → clear local session
if uid and not has_hint:
qs.pop("uid", None)
qs.pop("cart_sid", None)
qs.pop("sso_checked", None)
return
# Already logged in locally
if uid:
return
# No hint → nothing to do
if not has_hint:
return
# Has hint but no local session → trigger silent OAuth once
if qs.get("sso_checked"):
return
qs["sso_checked"] = True
return redirect(f"/auth/login/?next={_quote(request.url, safe='')}")
@app.before_request
async def _csrf_protect():
await protect()

View File

@@ -127,16 +127,23 @@ def create_oauth_blueprint(app_name: str) -> Blueprint:
qsession.clear()
resp = redirect("/")
resp.delete_cookie("blog_session", domain=".rose-ash.com", path="/")
resp.delete_cookie("sso_hint", domain=".rose-ash.com", path="/")
return resp
@bp.get("/sso-clear")
@bp.get("/sso-clear/")
async def sso_clear():
"""Clear local session, then redirect to next app in logout chain."""
qsession.pop(SESSION_USER_KEY, None)
qsession.pop("cart_sid", None)
next_url = request.args.get("next", "/")
return redirect(next_url)
@bp.post("/logout")
@bp.post("/logout/")
async def logout():
qsession.pop(SESSION_USER_KEY, None)
qsession.pop("cart_sid", None)
qsession.pop("sso_checked", None)
# Redirect through account to clear the SSO session too
# Redirect through account to clear all app sessions
return redirect(account_url("/auth/sso-logout/"))
return bp