diff --git a/cista/api.py b/cista/api.py index 9dd6c06..d19c06b 100644 --- a/cista/api.py +++ b/cista/api.py @@ -40,7 +40,9 @@ async def watch(req, ws): if sso.paskia_enabled(): # SSO auth: call validation to get user info (don't enforce auth in public mode) try: - await sso.validate_sso_request(req) + # WebSocket cannot forward Set-Cookie, so ask the auth backend not to + # renew the session here; renewal happens on the HTTP side instead. + await sso.validate_sso_request(req, renew=False) except Exception as e: logger.debug("watch SSO validation failed: %s", e) if sso_user := getattr(req.ctx, "sso_user", None): diff --git a/cista/sso.py b/cista/sso.py index 609ded2..e64bc7d 100644 --- a/cista/sso.py +++ b/cista/sso.py @@ -62,12 +62,18 @@ async def close_client(): _client = None -async def validate_sso_request(request, *, perm: str = "cista:login") -> dict | None: +async def validate_sso_request( + request, *, perm: str = "cista:login", renew: bool = True +) -> dict | None: """Validate an SSO request against the auth backend. Args: request: The Sanic request object perm: Permission to validate (default: cista:login, privileged also cista:admin) + renew: Whether to allow the auth backend to renew the session cookie. + Use ``False`` for WebSocket validation where Set-Cookie cannot be + forwarded to the client; this makes the request read-only and avoids + resetting the backend renewal timeout. Returns: User info dict if valid, None if validation fails with auth required response @@ -96,6 +102,8 @@ async def validate_sso_request(request, *, perm: str = "cista:login") -> dict | headers["x-forwarded-proto"] = request.scheme url = f"{PASKIA_BACKEND_URL}/auth/api/validate?perm={perm}" + if not renew: + url += "&renew=0" try: response = await client.post(