# Authentik forward-auth (via Traefik) Veola can delegate identity to Authentik using Traefik's `forwardAuth` middleware. Veola itself does not speak OIDC; it trusts identity headers that Authentik's proxy outpost sets, and matches/creates a local user by **email**. Local password login stays available as a break-glass fallback. ## 1. Veola config ```toml [auth] mode = "forward" trusted_proxies = ["172.18.0.0/16"] # CIDR(s) of the Traefik container/host admin_group = "veola-admins" # Authentik group that grants admin ``` `trusted_proxies` is mandatory in forward mode: forward-auth headers are only honored when the **direct** connection (before `X-Forwarded-For` rewriting) comes from one of these CIDRs. Without it the headers are spoofable. Use the address Traefik connects from (its Docker network range, or `127.0.0.1/32` if co-located). Header names default to Authentik's (`X-Authentik-Username`, `X-Authentik-Email`, `X-Authentik-Name`, `X-Authentik-Groups`); override under `[auth]` only if your outpost differs. ## 2. Authentik - Create a **Proxy Provider** in *forward auth (single application)* mode with the external host (e.g. `https://veola.example.com`). - Bind it to an Application, and bind the embedded/standalone outpost. - Create a group (default name `veola-admins`) and add operators who should be Veola admins. Everyone else lands as a regular user. Role is re-synced from the group on every request. ## 3. Traefik ```yaml http: middlewares: authentik: forwardAuth: address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik" trustForwardHeader: true authResponseHeaders: - X-authentik-username - X-authentik-email - X-authentik-name - X-authentik-groups # Strip client-supplied identity headers on ingress so only the outpost # can set them (runs before `authentik` on the protected router). authentik-strip: headers: customRequestHeaders: X-authentik-username: "" X-authentik-email: "" X-authentik-name: "" X-authentik-groups: "" X-authentik-uid: "" routers: veola: rule: "Host(`veola.example.com`)" service: veola middlewares: - authentik-strip - authentik tls: certResolver: letsencrypt # The outpost's own endpoints (callback, start, sign_out) must go straight # to Authentik, NOT through the forwardAuth-protected `veola` router. This # router has NO auth middleware and points at the authentik service. veola-outpost: rule: "Host(`veola.example.com`) && PathPrefix(`/outpost.goauthentik.io/`)" priority: 100 service: authentik tls: certResolver: letsencrypt services: authentik: loadBalancer: servers: - url: "http://authentik-server:9000/" ``` **Router priority — important.** Both routers match the Veola host, so the more specific `veola-outpost` must win. Traefik's *default* priority is the rule length, so the catch-all `veola` router (`Host(...)`) gets a priority equal to its length (e.g. `Host(`veola.parodia.dev`)` = 25). Authentik's docs example sets the outpost router to `priority: 15`, which **silently loses** whenever the hostname rule is longer than 15 chars — the catch-all then swallows `/outpost.goauthentik.io/*` and hands it to Veola, which 404s. Symptom: clicking **Sign out** lands on `/outpost.goauthentik.io/sign_out` with a plain `404 page not found`. Fix: give `veola-outpost` a high explicit priority (e.g. `100`) so it always beats the catch-all. (Login still works even when the outpost router loses, because the outpost intercepts `callback`/`start` during the forwardAuth `/auth/traefik` round-trip; `sign_out` is the one path that genuinely needs the dedicated router.) ## Break-glass `/login` and `/setup` still work for local password accounts. If Authentik is down, reach Veola directly (bypassing the proxy) from a trusted network and log in with a local admin account. Forward-auth is a no-op for connections that do not originate from `trusted_proxies`.