Files
paskia/docs/proxy/index.md
LeoVasanko 383c9f472e Add public access mode (public=1) to forward auth
/auth/api/forward?public=1 passes requests through with a Remote-Public
header (anonymous/forbidden/authenticated) instead of 401/403, so routes
can allow anonymous visitors while still identifying logged-in users.
Reauth (max_age) still requires the auth flow. Documented in Headers.md,
api/forward.md, Integration.md and all proxy guides.
2026-09-05 16:06:32 +00:00

3.6 KiB

Forward-Auth Proxy Guides

/auth/api/forward · Trusted headers

These guides show how to protect a backend application with Paskia using the forward-auth (also called "external authentication") mechanism. The reverse proxy asks Paskia whether a request is allowed before forwarding it to the protected service.

For details about the endpoint the proxy calls, see /auth/api/forward. For the headers your backend receives on successful requests, see Trusted Headers.

Available guides

  • Caddy — fully supported with ready-to-use snippets (auth/setup and auth/require).
  • Nginx — using the auth_request module.
  • Traefik — using the ForwardAuth middleware.
  • Apache APISIX — using the forward-auth plugin.
  • Envoy — using the ext_authz HTTP filter.
  • HAProxy — using a Lua auth request.

Common requirements

No matter which proxy you use, the auth subrequest must:

  1. Be sent to GET /auth/api/forward on the Paskia backend. By default Paskia listens on localhost:4401; set the AUTH_UPSTREAM environment variable in our Caddy snippets, or point your proxy at wherever Paskia is running.
  2. Include the query parameters Paskia needs for access control:
    • perm — required permission scope, repeatable (e.g. perm=myapp:login). See perm argument.
    • max_age — how recently the user must have authenticated (e.g. max_age=5min). See max_age argument.
    • public=1 — optional; allow public access (anonymous visitors and users missing perm pass through, marked with a Remote-Public header instead of a 401/403). See public access.
  3. Forward these request headers from the original client request:
    • Host — the site the user is visiting.
    • Cookie — the session cookie, normally __Host-paskia.
    • X-Forwarded-Method — the original HTTP method (e.g. GET, POST).
    • X-Forwarded-Uri — the original path and query string (e.g. /reports?foo=bar).
    • Accept — decides whether a 401/403 response should be HTML (browser) or JSON (API/fetch).
  4. Strip hop-by-hop headers (Connection, Upgrade, Transfer-Encoding, Keep-Alive, Proxy-Connection, TE) from the auth subrequest. The auth check is a plain HTTP request and must not carry WebSocket/body framing headers.
  5. On a 204 No Content response, copy the Remote-* response headers to the request that is forwarded to the protected backend. The headers are the whole point of the auth check. With public=1, also copy Remote-Public — it marks whether the request is authenticated, forbidden or anonymous, and the backend must check it.
  6. On a 401/403 response, send Paskia's response back to the client without contacting the protected backend. (With public=1 these only occur for reauth requirements.)
  7. Also proxy the /auth/ path prefix to Paskia so the login/profile UI, API endpoints, and WebSockets are reachable. Paskia's WebSocket endpoints need Upgrade and Connection headers passed through for that path.

Backend usage

After the proxy forwards the request, your backend can read the trusted headers. For example, in Python/FastAPI:

user_id = request.headers.get("Remote-User")
org_id = request.headers.get("Remote-Org")
permissions = request.headers.get("Remote-Groups", "").split(",")

Only trust headers that come from the proxy; never trust Remote-* headers that arrive directly from the internet. Your proxy configuration should strip any client-supplied Remote-* headers before the auth check.