Trusted proxies
Forward-auth headers like Remote-User are only meaningful when they are set by
a reverse proxy you trust. Any client that can reach your app directly could set
those headers itself and impersonate anyone. That is why the trusted-proxy
allowlist is mandatory in this library, and why Tinyauth's own docs warn:
Ensure a list of trusted proxy URLs is configured for the app. Accepting headers from untrusted proxies can lead to security vulnerabilities.
How the check works
TinyauthMiddleware reads request.client.host — the immediate TCP peer
that opened the connection — and checks it against the networks parsed from
TinyauthConfig.trusted_proxies. If the peer is not in any trusted network, the
request is rejected with HTTP 403 and the headers are ignored.
config = TinyauthConfig(trusted_proxies=["10.0.0.0/8", "127.0.0.1/32", "::1/128"])
Entries may be CIDR ranges (10.0.0.0/8) or bare addresses (10.0.0.5, treated
as a single host). IPv4 and IPv6 are both supported.
Important caveat: what the peer actually is
request.client.host is the address of whatever opened the TCP connection to
your app. In a typical deployment that is your reverse proxy, not the real end
user:
end user --> Tinyauth / reverse proxy --> your FastAPI app
(the peer your app sees)
So trusted_proxies should contain the address(es) of your proxy, not of
end users. This is exactly what you want: it ensures the identity headers were
attached by your proxy.
What this check does not do:
- It does not parse
X-Forwarded-For, so it cannot restrict which end users (behind the proxy) may reach the app. Do that with access controls on the proxy itself (Tinyauth's group rules, or firewall/ACLs). - It does not defend against a compromised or misconfigured proxy that forwards attacker-controlled headers.
Choosing values
- Docker / compose network: trust the proxy service's subnet, for example
172.16.0.0/12or the specific bridge network CIDR. - Same host (loopback):
127.0.0.1/32and::1/128. - Kubernetes: trust the pod/service CIDR that the ingress/auth proxy uses.
Prefer the narrowest range that still covers your proxy.
Testing note
Starlette's TestClient reports its peer host as the string "testclient",
which is not a valid IP and is therefore untrusted — every request would get a
403. Construct the client with an explicit trusted peer:
from starlette.testclient import TestClient
client = TestClient(app, client=("127.0.0.1", 12345))