diff --git a/bp/auth/routes.py b/bp/auth/routes.py index 94afc82..09b3266 100644 --- a/bp/auth/routes.py +++ b/bp/auth/routes.py @@ -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():