goauthentik / goauthentik/authentik
Standalone proxy outpost (2026.8.2, Rust): forward auth loops after callback; session id is never recognized and no session file is written
- Dominant language
- Python
- Stars
- 25.6k
- Forks
- 2k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 644
Description
**Describe the bug**
After upgrading server + external (standalone) proxy outpost to 2026.8.2, every `forward_single` Proxy Provider behind Traefik `forwardAuth` loops forever between `/application/o/authorize/` and `/outpost.goauthentik.io/callback`. The browser aborts with `ERR_TOO_MANY_REDIRECTS` ("The page isn't redirecting properly").
The token exchange succeeds. The outpost then redirects to the application root, and the very next `forward_auth` sub-request for that same cookie is treated as unauthenticated, so the outpost starts a new authorization. This repeats until the browser stops.
All six proxy providers on the outpost were affected. The embedded outpost on the **same** server container, with the **same** providers, works correctly — moving the `forwardAuth` address from the standalone container to the embedded outpost fixed every application immediately.
**Evidence**
Traefik access log for a protected host (`OriginStatus: 0` means the request never reached the backend, the middleware answered):
```
/outpost.goauthentik.io/callback?...&code= DownstreamStatus 302 -> /
/ OriginStatus 0 DownstreamStatus 302 -> /application/o/authorize/...
/outpost.goauthentik.io/callback?...&code= DownstreamStatus 302 -> /
/ OriginStatus 0 DownstreamStatus 302 -> /application/o/authorize/...
```
Packet capture inside the outpost's network namespace (the `forwardAuth` hop is plaintext HTTP) during one login:
```
GET /outpost.goauthentik.io/callback?X-authentik-auth-callback=true&code=...
Cookie: authentik_proxy_=%3D
X-Forwarded-Host: app.example.com
POST /application/o/token/ -> HTTP/1.1 200 OK
-> HTTP/1.1 302 Found location: https://app.example.com/
(no Set-Cookie on the callback response)
GET /outpost.goauthentik.io/auth/traefik
Cookie: authentik_proxy_=%3D (same value)
X-Forwarded-Host: app.example.com
-> HTTP/1.1 302 Found location: https://auth.example.com/application/o/authorize/...
Set-Cookie: authentik_proxy_=...
```
So the outpost hands out a fresh session id on every pass; it never recognizes the one it just authenticated.
No session file is ever created. `$TMPDIR` is `/dev/shm` in the official image, and it contains only:
```
authentik-metrics.sock
authentik-mode
session-cleanup.lock
```
`strace -f -e trace=openat,unlink,unlinkat,rename,write -p 1` on the outpost during a complete login shows exactly one session-related syscall, a read that misses:
```
openat(AT_FDCWD, "/dev/shm/session_", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
```
I could not determine **why** the session is not stored, and I want to be explicit about the limits of the evidence above:
- That syscall filter would miss a write issued through `pwrite`/`writev`/`openat2`/`io_uring`, so "no write happens" is not fully proven.
- With `AUTHENTIK_LOG_LEVEL=trace` the outpost logs startup only. It emits **no** per-request line, and in particular **no** warning, for the whole failing flow. If the callback handler errors, I would expect the `handlers/mod.rs` warning seen in #25875, and it never appears. Either the failure takes a different path or the outpost is silent here; I cannot distinguish the two from outside.
- Therefore the missing session file may be a symptom of an earlier abort in the callback handler rather than a session-store bug.
**To Reproduce**
1. Run 2026.8.2 server + worker + postgres, plus a **standalone** `ghcr.io/goauthentik/proxy:2026.8.2` container with `AUTHENTIK_HOST=http://server:9000`, `AUTHENTIK_HOST_BROWSER=https://auth.example.com`, `AUTHENTIK_INSECURE=false`, and an outpost token.
2. Create a Proxy Provider in `forward_single` mode, external host `https://app.example.com`, and assign it to that standalone outpost only.
3. Traefik middleware:
```yaml
authentik-forwardauth:
forwardAuth:
address: "http://authentik-proxy:9000/outpost.goauthentik.io/auth/traefik"
trustForwardHeader: true
authResponseHeaders: [X-authentik-username, X-authentik-groups, X-authentik-email, X-authentik-name, X-authentik-uid]
```
plus a router with `PathPrefix('/outpost.goauthentik.io/')` pointing at the same container, and the middleware on the application's router.
4. Open `https://app.example.com/`, sign in. The callback returns 302 to `/`, and `/` redirects to `/authorize` again, forever.
**Expected behavior**
After the callback, the `forward_auth` sub-request for the same cookie returns 200 with the `X-authentik-*` headers.
**Workaround**
Assign the providers to the embedded outpost and point the `forwardAuth` address and the `/outpost.goauthentik.io/` router at the server container (`http://server:9000`). Everything works at once, unchanged otherwise. Embedded-outpost sessions are in Postgres, which is consistent with a session-persistence problem specific to the standalone outpost.
**Version and Deployment**
- authentik 2026.8.2 (server, worker, standalone proxy outpost all on the same tag)
- Docker Compose on Linux; Traefik v3 in front; the outpost and Traefik share a user-defined bridge network in `172.16.0.0/12`, so forwarded headers come from a default-trusted CIDR
- Provider mode `forward_single`, `intercept_header_auth: true`, no cookie domain set, no certificate on the provider
- Server and outpost clocks agree to the second
**Additional context**
Possibly the same underlying regression as #25875 (post-callback redirect loop with the Rust outpost), though that report concerns the **embedded** outpost with an `https` `authentik_host`; here the embedded outpost is the thing that works and `authentik_host` is plaintext. Other post-rewrite proxy regressions: #25643, #25463, #25485.
I am happy to re-run the standalone outpost with a full syscall trace or any patched build and report back.
Contributor guide
Research direction
Start by comparing the standalone Rust outpost callback and session handling with the embedded outpost, using the reported forward-auth callback flow and missing session-file evidence. The issue does not identify source files or tests; first trace how the callback stores the session and how the next auth request loads it. Done means the same callback cookie yields a 200 response with the expected X-authentik-* headers instead of restarting authorization.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, python, rust
- Domain
- authentication, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100