Propagate login to all client apps via OAuth chain
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 53s

After magic link login, account bounces through each client app's
/auth/login to establish local sessions via OAuth. Each app does its
OAuth flow (instant since account is logged in) then redirects back
to /auth/propagate for the next app in the chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-23 12:40:08 +00:00
parent e0a2a47ba2
commit 6275049025

View File

@@ -230,8 +230,34 @@ def register(url_prefix="/auth"):
# Fresh account session ID for grant tracking
qsession[ACCOUNT_SESSION_KEY] = secrets.token_urlsafe(32)
# Propagate login to all client apps via OAuth chain
redirect_url = pop_login_redirect_target()
return redirect(redirect_url, 303)
qsession["sso_final"] = redirect_url
qsession["sso_chain"] = list(ALLOWED_CLIENTS)
return redirect(url_for("auth.propagate"), 303)
@auth_bp.get("/propagate")
@auth_bp.get("/propagate/")
async def propagate():
"""Chain through each client app's OAuth login to propagate the
account session. Each app does its OAuth flow (instant since
account is already logged in) then redirects back here. When
the chain is empty, redirect to the original target."""
chain = qsession.get("sso_chain", [])
final = qsession.get("sso_final", account_url("/"))
if not chain or not g.get("user"):
qsession.pop("sso_chain", None)
qsession.pop("sso_final", None)
return redirect(final)
next_app = chain.pop(0)
qsession["sso_chain"] = chain
from urllib.parse import quote
comeback = account_url("/auth/propagate")
login_url = app_url(next_app, f"/auth/login?next={quote(comeback, safe='')}")
return redirect(login_url)
@auth_bp.post("/logout/")
async def logout():