Skip dead apps in login propagation chain
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 46s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 46s
Health-check each app via internal URL before redirecting. Dead apps are silently skipped so the chain doesn't break. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -242,22 +242,44 @@ def register(url_prefix="/auth"):
|
|||||||
"""Chain through each client app's OAuth login to propagate the
|
"""Chain through each client app's OAuth login to propagate the
|
||||||
account session. Each app does its OAuth flow (instant since
|
account session. Each app does its OAuth flow (instant since
|
||||||
account is already logged in) then redirects back here. When
|
account is already logged in) then redirects back here. When
|
||||||
the chain is empty, redirect to the original target."""
|
the chain is empty, redirect to the original target.
|
||||||
|
Dead apps are skipped via internal health check."""
|
||||||
|
import os, aiohttp
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
chain = qsession.get("sso_chain", [])
|
chain = qsession.get("sso_chain", [])
|
||||||
final = qsession.get("sso_final", account_url("/"))
|
final = qsession.get("sso_final", account_url("/"))
|
||||||
|
|
||||||
if not chain or not g.get("user"):
|
if not g.get("user"):
|
||||||
qsession.pop("sso_chain", None)
|
qsession.pop("sso_chain", None)
|
||||||
qsession.pop("sso_final", None)
|
qsession.pop("sso_final", None)
|
||||||
return redirect(final)
|
return redirect(final)
|
||||||
|
|
||||||
next_app = chain.pop(0)
|
|
||||||
qsession["sso_chain"] = chain
|
|
||||||
|
|
||||||
from urllib.parse import quote
|
|
||||||
comeback = account_url("/auth/propagate")
|
comeback = account_url("/auth/propagate")
|
||||||
login_url = app_url(next_app, f"/auth/login?next={quote(comeback, safe='')}")
|
|
||||||
return redirect(login_url)
|
while chain:
|
||||||
|
next_app = chain.pop(0)
|
||||||
|
qsession["sso_chain"] = chain
|
||||||
|
|
||||||
|
# Health check via internal URL before redirecting
|
||||||
|
internal = (os.getenv(f"INTERNAL_URL_{next_app.upper()}") or f"http://{next_app}:8000").rstrip("/")
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as http:
|
||||||
|
async with http.head(
|
||||||
|
internal,
|
||||||
|
timeout=aiohttp.ClientTimeout(total=2),
|
||||||
|
allow_redirects=True,
|
||||||
|
) as resp:
|
||||||
|
if resp.status < 500:
|
||||||
|
login = app_url(next_app, f"/auth/login?next={quote(comeback, safe='')}")
|
||||||
|
return redirect(login)
|
||||||
|
except Exception:
|
||||||
|
current_app.logger.warning("[propagate] skipping dead app: %s", next_app)
|
||||||
|
continue
|
||||||
|
|
||||||
|
qsession.pop("sso_chain", None)
|
||||||
|
qsession.pop("sso_final", None)
|
||||||
|
return redirect(final)
|
||||||
|
|
||||||
@auth_bp.post("/logout/")
|
@auth_bp.post("/logout/")
|
||||||
async def logout():
|
async def logout():
|
||||||
|
|||||||
Reference in New Issue
Block a user