Files
paskia/docs/proxy/index.md
T
LeoVasanko 10af29f92d MultiSite: one instance serves authentication across many domains (#4)
- Serve multiple domains (RP IDs) from one instance: host-based dispatch,
  per-domain credentials and sessions, domains managed at runtime in the
  admin UI — previously one RP per instance
- Cross-domain sign-in via Related Origin Requests: per-domain related-origins
  list with a served .well-known/webauthn document
- Explicit per-domain origin lists with shell-glob wildcards (**. for apex +
  any subdomain depth, *. for one level), editable in the admin UI with
  validation and self-lockout guards
- Per-domain auth hosts: the account/admin UI can live on a different host
  per domain, no longer confined to subdomains of a single RP
- CLI: 'paskia init <rp-id [rp-name]' initializes or adds a domain to an
  existing database; 'paskia migrate' converts legacy databases

BREAKING CHANGES (v2.0):
- Database schema: config is now per-domain and credentials/sessions carry
  an rp_id — existing databases must be converted with 'paskia migrate'
- Origins are now explicit: main implicitly allowed every subdomain of the
  RP; configure '**.' origins to reproduce that behavior
- CLI: the flat '--rp-id/--rp-name/--origin/--auth/--save' flags are
  replaced by the 'init' and 'migrate' subcommandsReviewed-on: #4
2026-09-07 22:02:06 +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.