Files
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

5.1 KiB

GET /auth/api/forward

API overview · Proxy guides

Forward-auth validation for reverse proxies. The proxy calls this endpoint for every incoming request; Paskia validates the session and either authorizes the request by returning 204 with Remote-* headers, or rejects it with 401/403 error responses with an HTML login page if text/html was requested (i.e. it's a browser viewing the page), or otherwise JSON with details on the error and a URL to initiate API authentication flow.

The proxy server follows the 204 response with the original request to protected service, adding those remote headers to original user request, sent to the service. Any error response is sent directly back to client, never connecting to the protected service.

See Forward-Auth Proxy Guides for Caddy, Nginx, Traefik, Apache APISIX, Envoy and HAProxy configuration examples. Caddy users can also start from the dedicated Caddy configuration guide.

Query parameters

Parameter Description
perm Required permissions. See the perm argument.
max_age Require recent passkey use. See the max_age argument.
public public=1 allows public access: instead of 401 (no/expired session) or 403 (permission denied), the request passes with a Remote-Public header marking the bypass. Reauth (max_age) still requires the auth flow.

Public access

With public=1 the endpoint returns 204 in every case except reauth and malformed arguments, and always sets Remote-Public:

Value Meaning Identity headers
authenticated Session valid, all requested permissions met Full Remote-* set
forbidden Session valid, but the perm check failed Full Remote-* set (including Remote-Groups — it is trustworthy, it just lacks the requested permission)
anonymous No valid session None

The backend must check Remote-Public before treating the request as authorized. See Trusted Headers and the "Public access" section in the proxy guides.

Request headers

Header Expected value / note How Paskia uses it
Host Forwarded directly from the client Verifying the session's bound host
Cookie Forwarded directly or just cookie __Host-paskia Session ID
X-Forwarded-Method The HTTP method, e.g. POST Logging of original request
X-Forwarded-Uri Request path and query, e.g. /reports?foo=bar Logging of original request
Accept text/html or anything else Determines whether failures return an HTML page or JSON

Connection hop-by-hop headers (Connection, Upgrade, Transfer-Encoding, etc.) must not be forwarded.

Response

Success (204)

No response body. Only Remote headers are set on the response and your proxy should forward them to the backend request. The headers, not a response body, are the whole point of this endpoint: they are how the authenticated identity reaches the protected service, which can trust them because it is only reachable through the proxy.

Failure (400 / 401 / 403)

Status Meaning
400 Malformed perm argument (see perm). The error detail names /auth/api/forward as the origin and never echoes query arguments.
401 Session missing or expired, or max_age not satisfied — the user needs to (re)authenticate.
403 Requested permissions are missing — the forbidden flow allows signing in with another account.

Failure responses come in two flavors, chosen by the Accept header, because the audience differs:

  • A browser asking for a page (Accept includes text/html) gets a full authentication page it can show directly — the proxy simply passes the response through and the user can sign in without any application involvement.
  • Any other request (fetch, img, ...) gets JSON intended for programmatic handling. Besides the error detail, it carries an auth section whose iframe URL points to a ready-made authentication dialog your frontend can embed, so the user can sign in without leaving your app:
{
  "detail": "Additional authentication required",
  "auth": {
    "mode": "reauth",
    "iframe": "/auth/restricted/iframe#mode=reauth&theme=dark"
  }
}

The mode field:

  • login: no valid session, need to sign in
  • reauth: additional authentication required (using same passkey)
  • forbidden: lacking required permissions

Extra metadata such as the user's theme override may appear as additional fields and iframe fragment parameters.

Note: we provide a JavaScript package paskia with helpers for the embedding, fetch 401/403 handling, session renewals and more.

Session renewal

The forward endpoint does not renew the session because in the forward-auth mechanism it could not send the client a renewed session cookie. It only validates the current cookie and returns the trusted headers. Use /auth/api/validate when you need to refresh the session lifetime. Otherwise the user will have to sign in again every 24h even if they are actively using the service.