Remove PASKIA_SITE_URL env, use PASKIA_VITE_URL instead, moving the correct site URL determination to paskia CLI directly. This resolves devserver issues where VITE URL was reported as the external site URL instead of configured auth-host or origins. Main CLI still simplified. Bump deps versions and cleanup pyproject.toml.

This commit is contained in:
Leo Vasanko
2026-02-18 17:21:03 +00:00
parent cbfa68f3dc
commit 13b649a92b
3 changed files with 51 additions and 83 deletions
+18 -36
View File
@@ -6,7 +6,7 @@ import os
from urllib.parse import urlparse
from fastapi_vue import server
from fastapi_vue.hostutil import parse_endpoint
from fastapi_vue.hostutil import parse_endpoints
from paskia import db
from paskia import globals as _globals
@@ -56,6 +56,7 @@ def add_common_options(p: argparse.ArgumentParser) -> None:
"--origin",
action="append",
dest="origins",
default=[],
metavar="URL",
help="Allowed origin URL(s). May be specified multiple times. If any are specified, only those origins are permitted for WebSocket authentication.",
)
@@ -118,42 +119,25 @@ def main():
args.listen = stored_config.listen
# Parse first endpoint for config display and site_url
first_listen = args.listen[0] if isinstance(args.listen, list) else args.listen
endpoints = parse_endpoint(first_listen, DEFAULT_PORT)
ep = next(iter(parse_endpoints(args.listen, DEFAULT_PORT)), {})
host, port, uds = ep.get("host"), ep.get("port"), ep.get("uds")
# Extract host/port/uds from first endpoint for config display and site_url
ep = endpoints[0] if endpoints else {}
host = ep.get("host")
port = ep.get("port")
uds = ep.get("uds")
# Collect and normalize origins, handle auth_host
origins = [normalize_origin(o) for o in (getattr(args, "origins", None) or [])]
# Process and normalize auth_host
if args.auth_host:
# Normalize auth_host with scheme
if "://" not in args.auth_host:
args.auth_host = f"https://{args.auth_host}"
args.auth_host = args.auth_host.rstrip("/")
validate_auth_host(args.auth_host, args.rp_id)
args.origins.insert(0, args.auth_host) # Ensure first in origins
# If origins are configured, ensure auth_host is included at top
if origins:
# Insert auth_host at the beginning
origins.insert(0, args.auth_host)
# Remove duplicates while preserving order
seen = set()
origins = [x for x in origins if not (x in seen or seen.add(x))]
# Normalize, strip trailing slashes, and deduplicate while preserving order
origins = list({normalize_origin(o).rstrip("/"): ... for o in (args.origins)})
# Compute site_url and site_path for reset links
# Priority: PASKIA_SITE_URL (explicit) > auth_host > first origin with localhost > http://localhost:port
explicit_site_url = os.environ.get("PASKIA_SITE_URL")
if explicit_site_url:
# Explicit site URL from devserver or deployment config
site_url = explicit_site_url.rstrip("/")
site_path = "/" if args.auth_host else "/auth/"
elif args.auth_host:
site_url = args.auth_host.rstrip("/")
# Priority: auth_host > first configured origin > PASKIA_VITE_URL (devserver) > http://localhost:port > https://rp_id
site_path = "/auth/"
if args.auth_host:
site_url = args.auth_host
site_path = "/"
elif origins:
# Find localhost origin if rp_id is localhost, else use first origin
@@ -162,15 +146,13 @@ def main():
if args.rp_id == "localhost"
else None
)
site_url = (localhost_origin or origins[0]).rstrip("/")
site_path = "/auth/"
site_url = localhost_origin or origins[0]
elif vite_url := os.environ.get("PASKIA_VITE_URL"):
site_url = vite_url.rstrip("/") # Devserver
elif args.rp_id == "localhost" and port:
# Dev mode: use http with port
site_url = f"http://localhost:{port}"
site_path = "/auth/"
site_url = f"http://localhost:{port}" # Backend directly if we can
else:
site_url = f"https://{args.rp_id}"
site_path = "/auth/"
site_url = f"https://{args.rp_id}" # Assume external reverse proxy
# Build runtime configuration
config = PaskiaConfig(
+17 -27
View File
@@ -11,20 +11,29 @@ keywords = [ "forward_auth", "auth_request", "FastAPI" ]
authors = [
{name = "Leo Vasanko"},
]
requires-python = ">=3.11"
dependencies = [
"fastapi[standard]>=0.104.1",
"websockets>=12.0",
"webauthn>=1.11.1",
"base64url>=1.0.0",
"uuid7-standard>=1.0.0",
"pyjwt[crypto]>=2.8.0",
"fastapi[standard]>=0.129.0",
"websockets>=16.0",
"webauthn>=2.7.1",
"base64url>=1.1.1",
"uuid7-standard>=1.1.0",
"pyjwt[crypto]>=2.11.0",
"jsondiff>=2.2.1",
"msgspec>=0.20.0",
"aiofiles>=25.1.0",
"fastapi-vue>=0.3.0",
"fastapi-vue>=1.1.0",
"ua-parser[regex]>=1.0.1",
]
requires-python = ">=3.11"
[dependency-groups]
dev = [
"coverage>=7.13.4",
"httpx>=0.28.1",
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
"ruff>=0.15.1",
]
[project.urls]
Homepage = "https://git.zi.fi/LeoVasanko/paskia"
@@ -36,15 +45,6 @@ source = "vcs"
[tool.hatch.build.hooks.vcs]
version-file = "paskia/_version.py"
[project.optional-dependencies]
dev = [
"ruff>=0.1.0",
"coverage[toml]>=7.0.0",
"pytest>=8.0.0",
"pytest-asyncio>=0.24.0",
"httpx>=0.27.0",
]
[tool.coverage.run]
source = ["paskia"]
branch = true
@@ -75,16 +75,6 @@ select = ["E", "F", "I", "N", "W", "UP", "PLC0415"]
ignore = ["E501"] # Line too long
isort.known-first-party = ["paskia"]
[dependency-groups]
dev = [
"coverage>=7.12.0",
"httpx>=0.28.1",
"pytest>=9.0.1",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
"ruff>=0.14.8",
]
[project.scripts]
paskia = "paskia.__main__:main"
+16 -20
View File
@@ -153,29 +153,9 @@ async def run_devserver(args: argparse.Namespace, remaining: list[str]) -> None:
paskia.extend(["--origin", origin])
paskia.extend(remaining)
# Compute origins for Caddy
caddy_origins = []
if args.auth_host:
auth_host = args.auth_host
if "://" not in auth_host:
auth_host = f"https://{auth_host}"
caddy_origins.append(auth_host)
caddy_origins.append(f"https://{args.rp_id}")
if args.origins:
for origin in args.origins:
if "://" not in origin:
origin = f"https://{origin}"
caddy_origins.append(origin)
if not args.auth_host and not args.origins:
caddy_origins.append(f"https://{args.rp_id}")
# Remove duplicates while preserving order
seen = set()
caddy_origins = [x for x in caddy_origins if not (x in seen or seen.add(x))]
# Set environment for subprocesses
os.environ["PASKIA_VITE_URL"] = viteurl
os.environ["PASKIA_BACKEND_URL"] = backurl
os.environ["PASKIA_SITE_URL"] = caddy_origins[0] if args.caddy else viteurl
os.environ["PASKIA_DEV"] = "1"
if args.auth_host:
os.environ["PASKIA_AUTH_HOST"] = args.auth_host
@@ -183,6 +163,22 @@ async def run_devserver(args: argparse.Namespace, remaining: list[str]) -> None:
async with ProcessGroup() as pg:
# Start Caddy first if requested (needs to bind ports)
if args.caddy:
caddy_origins = []
if args.auth_host:
auth_host = args.auth_host
if "://" not in auth_host:
auth_host = f"https://{auth_host}"
caddy_origins.append(auth_host)
caddy_origins.append(f"https://{args.rp_id}")
if args.origins:
for origin in args.origins:
if "://" not in origin:
origin = f"https://{origin}"
caddy_origins.append(origin)
if not caddy_origins:
caddy_origins.append(f"https://{args.rp_id}")
seen: set = set()
caddy_origins = [x for x in caddy_origins if not (x in seen or seen.add(x))]
caddy_proc = await run_caddy(caddy_origins, viteurl, backurl)
pg._procs.append(caddy_proc)
pg._cmds[caddy_proc.pid] = "caddy"