goauthentik / goauthentik/authentik

Chunked request bodies silently dropped (breaks all logins via Cloudflare Tunnel)

Open
#24,165 4 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
25.6k
Forks
2k
Avg merge
1d 2h
Merged PRs (30d)
651

Description

### Describe the bug

POST request bodies sent with `Transfer-Encoding: chunked` (no `Content-Length`) are silently dropped before reaching the view layer. The request itself goes through — headers, method, and session are all intact — but the parsed body is empty.

The most visible symptom: **every login through a Cloudflare Tunnel fails** with `No identification data provided.` on the identification stage, because `cloudflared` always streams request bodies to the origin using chunked encoding. Clients that send `Content-Length` (browsers connecting directly, curl by default) are unaffected, which makes this look like a proxy or frontend problem when it is not.

### To reproduce

On any 2026.5.3 install, directly against the server container (no reverse proxy involved):

```bash
# Chunked: body is dropped -> "No identification data provided."
curl -s -X POST "http://localhost:9000/api/v3/flows/executor/default-authentication-flow/" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Transfer-Encoding: chunked" \
-d '{"component":"ak-stage-identification","uid_field":"someuser"}'

# Identical request with Content-Length: works (identification stage processes uid_field)
curl -s -X POST "http://localhost:9000/api/v3/flows/executor/default-authentication-flow/" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"component":"ak-stage-identification","uid_field":"someuser"}'
```

The chunked variant returns the identification challenge with `"response_errors": {"non_field_errors": [{"string": "No identification data provided.", "code": "invalid"}]}` and no `invalid_login` event is logged (the serializer never sees `uid_field`). The Content-Length variant logs `invalid_login` / proceeds normally.

**Isolation:** the same pair of requests sent directly to the gunicorn unix socket inside the container (bypassing the embedded Go proxy entirely) reproduces the same behavior:

```bash
docker exec ak_server curl -s --unix-socket /dev/shm/authentik-core.sock \
-X POST "http://localhost/api/v3/flows/executor/default-authentication-flow/" \
-H "Content-Type: application/json" -H "Transfer-Encoding: chunked" \
-d '{"component":"ak-stage-identification","uid_field":"someuser"}'
```

So the body is lost in the gunicorn (25.3.0) / uvicorn (0.46.0) `DjangoUvicornWorker` / ASGI layer, not in the Go proxy.

### Expected behavior

Chunked request bodies are read and parsed the same as bodies with `Content-Length`. RFC 9112-compliant HTTP/1.1 clients (including `cloudflared`, which always uses chunked streaming to the origin) can log in.

### Logs

Nothing is logged for the failing requests beyond the normal access log line (status 200). No `invalid_login`, no error, no traceback — the body loss is silent.

### Version and Deployment

- authentik version: 2026.5.3 (`ghcr.io/goauthentik/server:2026.5.3`, gunicorn 25.3.0, uvicorn 0.46.0)
- Deployment: docker-compose
- Not yet retested on 2026.5.4/2026.5.5, but the changelogs for both show no changes to body/transport handling.

### Additional context

- Real-world impact: `Cloudflare edge -> cloudflared -> reverse proxy -> authentik` breaks all POSTs (logins, flow submissions) while GETs work, since cloudflared streams bodies chunked. Reproduced with cloudflared 2026.5.0 and 2026.7.2.
- Workaround: a buffering reverse proxy in front of authentik (e.g. Traefik `buffering` middleware) converts chunked uploads to `Content-Length` and restores logins.
- Possibly the actual root cause of #19552 — same error, and reporters there are also behind Cloudflare proxying.

Contributor guide

Open the contributing guide

Research direction

Reproduce the paired curl requests against the server container and the gunicorn unix socket, then trace request-body handling through gunicorn 25.3.0, uvicorn 0.46.0, and DjangoUvicornWorker. Done means a request using Transfer-Encoding: chunked reaches the view layer with its JSON body parsed like the Content-Length variant, including through the authentication flow.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, python
Domain
api, authentication, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.