- 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
177 lines
6.1 KiB
Markdown
177 lines
6.1 KiB
Markdown
# Apache APISIX Forward-Auth
|
|
|
|
[`/auth/api/forward`](../api/forward.md) · [Proxy guides](index.md)
|
|
|
|
This guide uses the Apache APISIX [`forward-auth`](https://apisix.apache.org/docs/apisix/plugins/forward-auth/) plugin to ask Paskia whether each request is allowed.
|
|
|
|
## Overview
|
|
|
|
APISIX adds the standard `X-Forwarded-*` headers automatically, but we still tell the plugin to forward `Host`, `Cookie`, and `Accept` from the client request. On a `204` response from Paskia we copy the `Remote-*` headers to the backend request.
|
|
|
|
## Admin API example
|
|
|
|
```sh
|
|
# Route that proxies /auth/ to Paskia without forward-auth.
|
|
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
|
|
-H "X-API-KEY: ${admin_key}" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"id": "paskia-auth-ui",
|
|
"uri": "/auth/*",
|
|
"upstream": {
|
|
"nodes": { "localhost:4401": 1 },
|
|
"type": "roundrobin"
|
|
}
|
|
}'
|
|
|
|
# Protected route that uses Paskia forward-auth.
|
|
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
|
|
-H "X-API-KEY: ${admin_key}" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"id": "app-protected",
|
|
"uri": "/*",
|
|
"priority": 10,
|
|
"plugins": {
|
|
"forward-auth": {
|
|
"uri": "http://localhost:4401/auth/api/forward?perm=myapp:login",
|
|
"request_headers": [
|
|
"Host",
|
|
"Cookie",
|
|
"Accept",
|
|
"X-Forwarded-Method",
|
|
"X-Forwarded-Uri",
|
|
"X-Forwarded-Host",
|
|
"X-Forwarded-Proto",
|
|
"X-Forwarded-For"
|
|
],
|
|
"upstream_headers": [
|
|
"Remote-User",
|
|
"Remote-Name",
|
|
"Remote-Groups",
|
|
"Remote-Org",
|
|
"Remote-Org-Name",
|
|
"Remote-Role",
|
|
"Remote-Role-Name",
|
|
"Remote-Session-Expires",
|
|
"Remote-Credential",
|
|
"Remote-Public"
|
|
]
|
|
}
|
|
},
|
|
"upstream": {
|
|
"nodes": { "localhost:3000": 1 },
|
|
"type": "roundrobin"
|
|
}
|
|
}'
|
|
```
|
|
|
|
The `paskia-auth-ui` route has a higher priority (`priority` defaults to the same value for both routes; you can also rely on the more specific `/auth/*` URI matching first). Because it does not use the `forward-auth` plugin, users can reach the login/profile pages without already being authenticated.
|
|
|
|
## ADC / declarative example
|
|
|
|
```yaml
|
|
services:
|
|
- name: paskia-auth-ui
|
|
routes:
|
|
- name: auth-route
|
|
uris:
|
|
- /auth/*
|
|
upstream:
|
|
type: roundrobin
|
|
nodes:
|
|
- host: localhost
|
|
port: 4401
|
|
weight: 1
|
|
|
|
- name: app-protected
|
|
routes:
|
|
- name: app-route
|
|
uris:
|
|
- /*
|
|
plugins:
|
|
forward-auth:
|
|
uri: http://localhost:4401/auth/api/forward?perm=myapp:login
|
|
request_headers:
|
|
- Host
|
|
- Cookie
|
|
- Accept
|
|
- X-Forwarded-Method
|
|
- X-Forwarded-Uri
|
|
- X-Forwarded-Host
|
|
- X-Forwarded-Proto
|
|
- X-Forwarded-For
|
|
upstream_headers:
|
|
- Remote-User
|
|
- Remote-Name
|
|
- Remote-Groups
|
|
- Remote-Org
|
|
- Remote-Org-Name
|
|
- Remote-Role
|
|
- Remote-Role-Name
|
|
- Remote-Session-Expires
|
|
- Remote-Credential
|
|
- Remote-Public
|
|
upstream:
|
|
type: roundrobin
|
|
nodes:
|
|
- host: localhost
|
|
port: 3000
|
|
weight: 1
|
|
```
|
|
|
|
Apply it with:
|
|
|
|
```sh
|
|
adc sync -f paskia.yaml
|
|
```
|
|
|
|
## What APISIX sends to Paskia
|
|
|
|
APISIX automatically adds these headers to the auth request:
|
|
|
|
| Header | Value |
|
|
|---|---|
|
|
| `X-Forwarded-Method` | Original HTTP method |
|
|
| `X-Forwarded-Proto` | Request scheme (`http`/`https`) |
|
|
| `X-Forwarded-Host` | Original host |
|
|
| `X-Forwarded-Uri` | Original request URI |
|
|
| `X-Forwarded-For` | Client IP address |
|
|
|
|
We list them again in `request_headers` to make sure they are not accidentally filtered out when the list is explicit.
|
|
|
|
## Response headers
|
|
|
|
`upstream_headers` lists the `Remote-*` headers that APISIX copies from the auth response to the backend request. The plugin does not support a wildcard here, so each header must be named.
|
|
|
|
If you want Paskia's failure-response headers (such as `Content-Type` or `Set-Cookie`) to reach the client, list them in `client_headers`. For Paskia this is usually not needed; the response body already contains the JSON auth URL or the HTML login page.
|
|
|
|
## Adjusting requirements
|
|
|
|
Change the `uri` query string to require different permissions or recent authentication:
|
|
|
|
```yaml
|
|
uri: http://localhost:4401/auth/api/forward?perm=myapp:login
|
|
uri: http://localhost:4401/auth/api/forward?perm=myapp:admin&max_age=5min
|
|
uri: http://localhost:4401/auth/api/forward
|
|
```
|
|
|
|
The last form requires only authentication. See [perm argument](../api/perm.md) and [max_age argument](../api/max-age.md).
|
|
|
|
## Public access
|
|
|
|
For routes where anonymous visitors are allowed but logged-in users should still be identified, add `public=1` to the `forward-auth` URI:
|
|
|
|
```yaml
|
|
uri: http://localhost:4401/auth/api/forward?public=1
|
|
uri: http://localhost:4401/auth/api/forward?public=1&perm=myapp:reports
|
|
```
|
|
|
|
The auth check then always returns 204 (except reauth with `max_age`, which still returns the 401 auth flow), and the `Remote-Public` header — included in the `upstream_headers` lists above — marks each request as `anonymous`, `forbidden` or `authenticated`. The backend always runs and must check `Remote-Public` before treating the request as authorized. See [public access](../api/forward.md#public-access) and [trusted headers](../Headers.md#public-access).
|
|
|
|
## Notes
|
|
|
|
- The auth request is `GET` by default. Since the `forward-auth` plugin does not forward the request body unless `request_method` is set to `POST`, the default `GET` is the right choice for Paskia.
|
|
- Hop-by-hop headers are handled by APISIX when it builds the auth request, so no extra configuration is needed for `Connection`/`Upgrade`.
|
|
- If Paskia is running on a different host, replace `localhost:4401` with the Paskia service address. For a dedicated authentication host (the domain's auth-host setting), route `auth.example.com` to Paskia instead of `/auth/`.
|