feat: env-driven Secure flag on portal session cookie

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 00:16:54 +00:00
parent 01180d5725
commit 20f62a5c0a
4 changed files with 23 additions and 4 deletions
+2 -2
View File
@@ -417,14 +417,14 @@ async def project_detail_page(request: Request, project_id: str):
async def project_portal_preview(project_id: str, db: Session = Depends(get_db)):
"""Operator testing shortcut: open this project's client portal (no CLI)."""
from backend.models import Project
from backend.portal_auth import mint_portal_session, make_session_cookie, COOKIE_NAME, COOKIE_MAX_AGE
from backend.portal_auth import mint_portal_session, make_session_cookie, COOKIE_NAME, COOKIE_MAX_AGE, COOKIE_SECURE
project = db.query(Project).filter_by(id=project_id).first()
if not project:
return JSONResponse(status_code=404, content={"detail": "Project not found"})
token_id = mint_portal_session(project, db)
resp = RedirectResponse(url="/portal", status_code=303)
resp.set_cookie(COOKIE_NAME, make_session_cookie(token_id),
max_age=COOKIE_MAX_AGE, httponly=True, samesite="lax")
max_age=COOKIE_MAX_AGE, httponly=True, samesite="lax", secure=COOKIE_SECURE)
return resp
+3
View File
@@ -39,6 +39,9 @@ if SECRET_KEY == "dev-insecure-change-me":
COOKIE_NAME = "portal_session"
COOKIE_MAX_AGE = 60 * 60 * 24 * 30 # 30 days
# Set COOKIE_SECURE=true once the portal is served over HTTPS (TLS terminates at
# the Synology reverse proxy). Default false so plain-HTTP dev still works.
COOKIE_SECURE = os.getenv("COOKIE_SECURE", "false").lower() in ("1", "true", "yes")
class PortalAuthError(Exception):
+2 -2
View File
@@ -25,7 +25,7 @@ from backend.models import Client, MonitoringLocation, Project, UnitAssignment
from backend.templates_config import templates
from backend.portal_auth import (
get_current_client, client_from_cookie, make_session_cookie,
COOKIE_NAME, COOKIE_MAX_AGE,
COOKIE_NAME, COOKIE_MAX_AGE, COOKIE_SECURE,
resolve_project_by_link_token, mint_portal_session,
is_locked, register_failure, clear_failures,
)
@@ -156,7 +156,7 @@ def portal_password_submit(link_token: str, request: Request,
token_id = mint_portal_session(project, db)
resp = RedirectResponse(url="/portal", status_code=303)
resp.set_cookie(COOKIE_NAME, make_session_cookie(token_id),
max_age=COOKIE_MAX_AGE, httponly=True, samesite="lax")
max_age=COOKIE_MAX_AGE, httponly=True, samesite="lax", secure=COOKIE_SECURE)
logger.info(f"[PORTAL] password ok for project {project.id[:8]} → session opened")
return resp