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:
@@ -123,40 +123,6 @@ def create_base_app(
|
|||||||
for fn in before_request_fns:
|
for fn in before_request_fns:
|
||||||
app.before_request(fn)
|
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
|
@app.before_request
|
||||||
async def _csrf_protect():
|
async def _csrf_protect():
|
||||||
await protect()
|
await protect()
|
||||||
|
|||||||
@@ -127,16 +127,23 @@ def create_oauth_blueprint(app_name: str) -> Blueprint:
|
|||||||
qsession.clear()
|
qsession.clear()
|
||||||
resp = redirect("/")
|
resp = redirect("/")
|
||||||
resp.delete_cookie("blog_session", domain=".rose-ash.com", path="/")
|
resp.delete_cookie("blog_session", domain=".rose-ash.com", path="/")
|
||||||
resp.delete_cookie("sso_hint", domain=".rose-ash.com", path="/")
|
|
||||||
return resp
|
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")
|
||||||
@bp.post("/logout/")
|
@bp.post("/logout/")
|
||||||
async def logout():
|
async def logout():
|
||||||
qsession.pop(SESSION_USER_KEY, None)
|
qsession.pop(SESSION_USER_KEY, None)
|
||||||
qsession.pop("cart_sid", None)
|
qsession.pop("cart_sid", None)
|
||||||
qsession.pop("sso_checked", None)
|
# Redirect through account to clear all app sessions
|
||||||
# Redirect through account to clear the SSO session too
|
|
||||||
return redirect(account_url("/auth/sso-logout/"))
|
return redirect(account_url("/auth/sso-logout/"))
|
||||||
|
|
||||||
return bp
|
return bp
|
||||||
|
|||||||
Reference in New Issue
Block a user