Iframe-based SSO logout (tolerates dead apps)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 48s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 48s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -201,7 +201,6 @@ def register(url_prefix="/auth"):
|
|||||||
@auth_bp.post("/logout/")
|
@auth_bp.post("/logout/")
|
||||||
async def logout():
|
async def logout():
|
||||||
qsession.pop(SESSION_USER_KEY, None)
|
qsession.pop(SESSION_USER_KEY, None)
|
||||||
# Chain through all client apps to clear their sessions too
|
|
||||||
return redirect(url_for("auth.sso_logout"))
|
return redirect(url_for("auth.sso_logout"))
|
||||||
|
|
||||||
@auth_bp.get("/clear/")
|
@auth_bp.get("/clear/")
|
||||||
@@ -214,21 +213,25 @@ def register(url_prefix="/auth"):
|
|||||||
|
|
||||||
@auth_bp.get("/sso-logout/")
|
@auth_bp.get("/sso-logout/")
|
||||||
async def sso_logout():
|
async def sso_logout():
|
||||||
"""SSO logout: clear account session, then chain through all client
|
"""SSO logout: clear account session, then render a page with hidden
|
||||||
apps so each clears its own first-party session cookie."""
|
iframes that clear each client app's first-party session cookie.
|
||||||
|
Tolerates dead apps — iframes that fail are silently ignored."""
|
||||||
qsession.pop(SESSION_USER_KEY, None)
|
qsession.pop(SESSION_USER_KEY, None)
|
||||||
|
|
||||||
from shared.infrastructure.urls import blog_url, market_url, cart_url, events_url, federation_url
|
from shared.infrastructure.urls import blog_url, market_url, cart_url, events_url, federation_url
|
||||||
from urllib.parse import quote
|
|
||||||
|
|
||||||
# Build redirect chain: blog → market → cart → events → federation → blog home
|
clear_urls = [
|
||||||
final = blog_url("/")
|
blog_url("/auth/sso-clear"),
|
||||||
chain = federation_url(f"/auth/sso-clear?next={quote(final, safe='')}")
|
market_url("/auth/sso-clear"),
|
||||||
chain = events_url(f"/auth/sso-clear?next={quote(chain, safe='')}")
|
cart_url("/auth/sso-clear"),
|
||||||
chain = cart_url(f"/auth/sso-clear?next={quote(chain, safe='')}")
|
events_url("/auth/sso-clear"),
|
||||||
chain = market_url(f"/auth/sso-clear?next={quote(chain, safe='')}")
|
federation_url("/auth/sso-clear"),
|
||||||
chain = blog_url(f"/auth/sso-clear?next={quote(chain, safe='')}")
|
]
|
||||||
|
|
||||||
return redirect(chain)
|
return await render_template(
|
||||||
|
"auth/signing_out.html",
|
||||||
|
clear_urls=clear_urls,
|
||||||
|
final_url=blog_url("/"),
|
||||||
|
)
|
||||||
|
|
||||||
return auth_bp
|
return auth_bp
|
||||||
|
|||||||
2
shared
2
shared
Submodule shared updated: a93a456ac5...9a637c6227
21
templates/auth/signing_out.html
Normal file
21
templates/auth/signing_out.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% extends "_types/root/_index.html" %}
|
||||||
|
{% block meta %}{% endblock %}
|
||||||
|
{% block title %}Signing out — Rose Ash{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="py-8 max-w-md mx-auto text-center">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Signing out…</h1>
|
||||||
|
<p class="text-stone-500 text-sm">You will be redirected shortly.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Fire-and-forget: clear each client app's session via hidden iframes #}
|
||||||
|
{% for url in clear_urls %}
|
||||||
|
<iframe src="{{ url }}" style="display:none" aria-hidden="true"></iframe>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Give iframes a moment to load, then redirect
|
||||||
|
setTimeout(function() {
|
||||||
|
window.location.href = "{{ final_url }}";
|
||||||
|
}, 1500);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user